package com.qh.pay.service.impl;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;

import com.alibaba.fastjson.JSONArray;
import com.qh.common.utils.CryptoPayUtil;
import com.qh.pay.api.PayApi;
import com.qh.pay.api.constenum.*;
import com.qh.pay.api.utils.*;
import com.qh.pay.dao.*;
import com.qh.pay.domain.*;
import com.qh.pay.service.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.NameValuePair;
import org.apache.http.client.fluent.Request;
import org.apache.http.message.BasicNameValuePair;
import org.redisson.api.RLock;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qh.common.config.CfgKeyConst;
import com.qh.common.config.Constant;
import com.qh.common.service.UserBankService;
import com.qh.common.utils.R;
import com.qh.pay.api.Order;
import com.qh.pay.api.PayConstants;
import com.qh.paythird.PayBaseService;
import com.qh.redis.RedisConstants;
import com.qh.redis.constenum.ConfigParent;
import com.qh.redis.service.RedisMsg;
import com.qh.redis.service.RedisUtil;
import com.qh.redis.service.RedissonLockUtil;
import sun.management.resources.agent;

/**
 * @version 1.0.0
 * @ClassName PayServiceImpl
 * @Description 支付实现类
 * @Date 2017年11月6日 下午2:48:20
 */
@Service
public class PayServiceImpl implements PayService {
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(PayServiceImpl.class);
    @Autowired
    private PayConfigCompanyService payCfgCompService;
    @Autowired
    private PayBaseService payBaseService;
    @Autowired
    private PayOrderQrDao payOrderQrDao;
    @Autowired
    private PayOrderLoseDao payOrderLoseDao;

    @Autowired
    private MerchantService merchantService;
    @Autowired
    private AgentService agentService;
    @Autowired
    private PayHandlerService payHandlerService;
    @Autowired
    private PayOrderDao payOrderDao;
    @Autowired
    private RecordFoundAcctDao rdFoundAcctDao;
    @Autowired
    private RecordMerchBalDao rdMerchBalDao;
    @Autowired
    private PayAuditDao payAuditDao;
    @Autowired
    private PayOrderAcpDao payOrderAcpDao;
    @Autowired
    private RecordMerchAvailBalDao rdMerchAvailBalDao;
    @Autowired
    private RecordFoundAvailAcctDao rdFoundAvailAcctDao;
    @Autowired
    private RecordPayMerchAvailBalDao rdPayMerchAvailBalDao;
    @Autowired
    private RecordPayMerchBalDao rdPayMerchBalDao;
    @Autowired
    private PayQrService payQrService;
    @Autowired
    private UserBankService userBankService;


    @Override
    public Object outChannal(Merchant merchant, JSONObject jo) {
        if (StringUtils.isEmpty(jo.getString(OrderParamKey.outChannel.name()))) {
            return R.error("通道类型为空");
        }
        List<String> list = RedisUtil.getMonAmount(jo.getString(OrderParamKey.outChannel.name()));
        if (list.isEmpty()) {
            return R.error("无可用通道");
        }
        Map<String, String> data = new HashMap<>();
        data.put(OrderParamKey.amount.name(), JSONArray.toJSON(list).toString());
        data.put(OrderParamKey.merchNo.name(), merchant.getMerchNo());
        return decryptAndSign(jo.getString(OrderParamKey.encryptType.name()), data, merchant.getPublicKey());

    }

    /**
     * 发起支付
     */
    @Override
    public Object order(Merchant merchant, JSONObject jo) {
        String merchNo = merchant.getMerchNo();
        String orderNo = jo.getString(OrderParamKey.orderNo.name());
        RLock lock = RedissonLockUtil.getOrderLock(merchNo + RedisConstants.link_symbol + orderNo);
        if (lock.tryLock()) {
            try {
                Order order = new Order();
                order.setOrderType(OrderType.pay.id());
                // 渠道编码
                order.setOutChannel(jo.getString(OrderParamKey.outChannel.name()));
                String initResult = null;
                if (OutChannel.jfDesc().containsKey(order.getOutChannel())) {
                    //初始化扫码通道订单
                    initResult = payHandlerService.initQrOrder(order, jo);
                } else {
                    // 初始化订单信息（银行卡）
                    initResult = payHandlerService.initOrder(order, jo);
                }
                if (ParamUtil.isNotEmpty(initResult)) {
                    logger.error(initResult);
                    return R.error(initResult);
                }
                //userId不能为商户号
                if (ParamUtil.isNotEmpty(order.getUserId()) && merchNo.equals(order.getUserId())) {
                    return R.error("userId不能与商户号一致");
                }

                if (RedisUtil.getOrder(merchNo, orderNo) != null) {
                    logger.error(merchNo + "," + orderNo + "订单号已经存在！");
                    return R.error(merchNo + "," + orderNo + "订单号已经存在！");
                } else if (payOrderDao.get(orderNo, merchNo) != null) {
                    logger.error(merchNo + "," + orderNo + "订单号已经存在！");
                    return R.error(merchNo + "," + orderNo + "订单号已经存在！");
                }
                R r;
//				if (OutChannel.jfDesc().containsKey(order.getOutChannel())) {
//					//扫码通道订单处理
                r = payQrService.qrOrder(order, merchant);
                if (R.ifError(r) && order.getRealAmount() != null && order.getRealAmount().compareTo(BigDecimal.ZERO) > 0) {
                    payQrService.releaseMonAmount(order);
                }
//					order.setPayCompany(PayCompany.jf.name());
//				}else{
//					//检查支付规则
//					r = this.checkCfgComp(order);
//					if(R.ifError(r)){
//						return r;
//					}
//					r = (R) payBaseService.order(order);
//				}

                //返回处理
                if (R.ifSucc(r)) {
                    @SuppressWarnings("unchecked")
                    Map<String, String> data = (Map<String, String>) r.get(Constant.result_data);
                    order.setResultMap(data);
                    RedisUtil.setOrder(order);
                    RedisUtil.getRedisNotifyTemplate().opsForValue().set(RedisConstants.cache_keyevent_not_pay_ord + merchNo + RedisConstants.link_symbol + orderNo, RedisConstants.keyevent_40, RedisConstants.keyevent_40, TimeUnit.MINUTES);
                    return decryptAndSign(jo.getString(OrderParamKey.encryptType.name()), data,
                            merchant.getPublicKey()).put(Constant.result_msg,
                            r.get(Constant.result_msg));
                }
                return r;
            } finally {
                lock.unlock();
            }
        } else {
            return R.error(merchNo + "," + orderNo + "下单失败！");
        }
    }


    /**
     * @param order
     * @return
     * @Description 支付通道 选择
     */
    private R checkCfgComp(Order order) {

        Merchant merchant = merchantService.get(order.getMerchNo());
        Integer mPayChannelType = merchant.getPayChannelType();
        //判断商户是否有分配  通道分类   没有分配不支持支付
        if (mPayChannelType == null || !PayChannelType.desc().containsKey(mPayChannelType)) {
            return R.error(order.getMerchNo() + "," + order.getOutChannel() + "未分配支付通道");
        }

        List<Object> payCfgComps = payCfgCompService.getPayCfgCompByOutChannel(order.getOutChannel());
        if (payCfgComps == null || payCfgComps.size() == 0) {
            return R.error(order.getMerchNo() + "," + order.getOutChannel() + "通道配置错误！");
        }

        List<PayConfigCompanyDO> pccList = new ArrayList<>();
        PayConfigCompanyDO payCfgComp = null;
        for (Object object : payCfgComps) {
            payCfgComp = (PayConfigCompanyDO) object;
            if (payCfgComp.getIfClose() == 0) {
                Integer cPayChannelType = payCfgComp.getPayChannelType();
                Integer paymentMethod = payCfgComp.getPaymentMethod();
                pccList.add(payCfgComp);
            }
        }

        if (pccList.size() <= 0) {
            //当前商户没有 同类通道
            return R.error(order.getMerchNo() + "," + order.getOutChannel() + "未找到相应的支付通道");
        }

        //同类 通道只有一个
        payCfgComp = pccList.get(0);
        if (payCfgComp == null) {
            return R.error(order.getMerchNo() + "," + order.getOutChannel() + "未找到相应的支付通道");
        }
        Integer maxPayAmt = payCfgComp.getMaxPayAmt();
        Integer minPayAmt = payCfgComp.getMinPayAmt();
        if (maxPayAmt != null && new BigDecimal(maxPayAmt).compareTo(order.getAmount()) == -1) {
            return R.error("单笔最高限额:" + maxPayAmt + "元");
        }
        if (minPayAmt != null && new BigDecimal(minPayAmt).compareTo(order.getAmount()) == 1) {
            return R.error("单笔最低限额为:" + minPayAmt + "元");
        }
        order.setPayCompany(payCfgComp.getCompany());
        order.setPayMerch(payCfgComp.getPayMerch());
        order.setCallbackDomain(payCfgComp.getCallbackDomain());
        return R.ok();
    }


    /*
     * (非 Javadoc) Description:
     *
     * @see com.qh.pay.service.PayService#notify(java.lang.String,
     * java.lang.String, java.lang.String,
     * javax.servlet.http.HttpServletRequest, java.lang.String)
     */
    @Override
    public R notify(String merchNo, String orderNo, HttpServletRequest request, String requestBody) {
        RLock lock = RedissonLockUtil.getOrderLock(merchNo + RedisConstants.link_symbol + orderNo);
        try {
            lock.lock();
            Order order = RedisUtil.getOrder(merchNo, orderNo);
            if (order != null && (order.getLastLockTime() == null || DateUtil.getCurrentTimeInt() - order.getLastLockTime() > 20)) {
                R r = payBaseService.notify(order, request, requestBody);
                order.setMsg((String) r.get(Constant.result_msg));
                order.setLastLockTime(DateUtil.getCurrentTimeInt());
                RedisUtil.setOrder(order);
                lock.unlock();
                RedisMsg.orderNotifyMsg(merchNo, orderNo);
                return r;
            }

        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return R.error("无效的结果");
    }

    /*
     * (非 Javadoc) Description:
     *
     * @see com.qh.pay.service.PayService#orderNotifyMsg(java.lang.String)
     */
    @Override
    public String orderNotifyMsg(String merchNo, String orderNo) {
//        RLock lock = RedissonLockUtil.getOrderLock(merchNo, orderNo);
//        if (lock.tryLock()) {
//            try {
                String result = orderNotify(merchNo, orderNo);
//                lock.unlock();
                return result;
//            } finally {
//                if (lock.isHeldByCurrentThread())
//                    lock.unlock();
//            }
//        } else {
//            logger.info("orderNotifyMsg，未获取到锁，{}，{}", merchNo, orderNo);
//        }
//        return null;
    }

    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayService#eventOrderNotifyMsg(java.lang.String, java.lang.String)
     */
    @Override
    public String eventOrderNotifyMsg(String merchNo, String orderNo) {
        RLock lock = RedissonLockUtil.getEventOrderLock(merchNo, orderNo);
        if (lock.tryLock()) {
            try {
                String result = orderNotify(merchNo, orderNo);
                lock.unlock();
                return result;
            } finally {
                if (lock.isHeldByCurrentThread())
                    lock.unlock();
            }
        } else {
            logger.info("eventOrderNotifyMsg，未获取到锁，{}，{}", merchNo, orderNo);
        }
        return null;
    }

    /**
     * @param merchNo
     * @param orderNo
     * @return
     * @Description 通知保存
     */
    private String orderNotify(String merchNo, String orderNo) {
        return orderNotify(RedisUtil.getOrder(merchNo, orderNo));
    }

    /**
     * @param order
     * @return
     * @Description 订单通知
     */
    public void orderNotifySync(Order order) {
        if (order == null) {
            return ;
        }
        String stateDesc = OrderState.desc().get(order.getOrderState());
        String result = null;
        logger.info("发送通知请求：{},{},{},{}", order.getNotifyUrl(), order.getMerchNo(), order.getOrderNo(), stateDesc);
        if (OrderState.init.id() == order.getOrderState()) {
            //RequestUtils.doPostJson(order.getNotifyUrl(), R.error(order.getMsg()).jsonStr());
            logger.info("{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo(), result);
        } else {
            Map<String, String> data = PayService.initRspData(order);
            data.put(OrderParamKey.orderState.name(), String.valueOf(order.getOrderState()));
            data.put(OrderParamKey.merchNo.name(), order.getMerchNo());
            data.put(OrderParamKey.amount.name(), order.getAmount().toString());
            data.put(OrderParamKey.orderNo.name(), order.getOrderNo());
//            Merchant merchant = merchantService.get(order.getMerchNo());
//            result = RequestUtils.doPostJson(order.getNotifyUrl(),
//                    decryptAndSign(data, merchant.getPublicKey(), order.getMsg()).jsonStr());

            result = RequestUtils.doPostJson(order.getNotifyUrl(),
                    decryptAndSignMD5(data, order.getMsg()).jsonStr());
            logger.info("{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo(), result);
            // todo 通知商户返回
            if (result.contains("ok") || result.contains("success")) {
                result = Constant.result_msg_ok;
                order.setNoticeState("1");

            }
        }
    }
    public String orderNotify(Order order) {
        if (order == null) {
            return null;
        }
        String stateDesc = OrderState.desc().get(order.getOrderState());
        String result = null;
        logger.info("发送通知请求：{},{},{},{}", order.getNotifyUrl(), order.getMerchNo(), order.getOrderNo(), stateDesc);
        if (OrderState.init.id() == order.getOrderState()) {
            //RequestUtils.doPostJson(order.getNotifyUrl(), R.error(order.getMsg()).jsonStr());
            logger.info("{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo(), result);
        } else {
            Map<String, String> data = PayService.initRspData(order);
            data.put(OrderParamKey.orderState.name(), String.valueOf(order.getOrderState()));
            data.put(OrderParamKey.merchNo.name(), order.getMerchNo());
            data.put(OrderParamKey.amount.name(), order.getAmount().toString());
            data.put(OrderParamKey.orderNo.name(), order.getOrderNo());
//            Merchant merchant = merchantService.get(order.getMerchNo());
//            result = RequestUtils.doPostJson(order.getNotifyUrl(),
//                    decryptAndSign(data, merchant.getPublicKey(), order.getMsg()).jsonStr());

            result = RequestUtils.doPostJson(order.getNotifyUrl(),
                    decryptAndSignMD5(data, order.getMsg()).jsonStr());
            logger.info("{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo(), result);
            // todo 通知商户返回
            if (result.contains("ok") || result.contains("success")) {
              result = Constant.result_msg_ok;
              order.setNoticeState("1");

            }
        }
        return result;
    }

    /**
     * @param order
     * @return
     * @Description 订单通知
     */
    private String orderNotify(Order order, String notifyUrl) {
        if (order == null) {
            return null;
        }
        String stateDesc = OrderState.desc().get(order.getOrderState());
        String result = null;
        if (StringUtils.isBlank(notifyUrl))
            notifyUrl = order.getNotifyUrl();
        logger.info("发送通知请求：{},{},{},{}", notifyUrl, order.getMerchNo(), order.getOrderNo(), stateDesc);
        if (OrderState.init.id() == order.getOrderState()) {
            //RequestUtils.doPostJson(order.getNotifyUrl(), R.error(order.getMsg()).jsonStr());
            logger.info("{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo(), result);
        } else {
            Map<String, String> data = PayService.initRspData(order);
            data.put(OrderParamKey.orderState.name(), String.valueOf(order.getOrderState()));
            data.put(OrderParamKey.businessNo.name(), order.getBusinessNo());
            data.put(OrderParamKey.amount.name(), order.getRealAmount().toString());
            data.put(OrderParamKey.orderNo.name(), order.getOrderNo());
            Merchant merchant = merchantService.get(order.getMerchNo());
            result = RequestUtils.doPostJson(notifyUrl,
                    decryptAndSign(data, merchant.getPublicKey(), order.getMsg()).jsonStr());
            logger.info("{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo(), result);
            if (result.contains("ok") || result.contains("success")) {
                result = Constant.result_msg_ok;
            }
        }
        return result;
    }

    /*
     * (非 Javadoc) Description:
     *
     * @see com.qh.pay.service.PayService#orderDataMsg(java.lang.String,java.lang.String)
     */
    @Override
    public void orderDataMsg(String merchNo, String orderNo) {




//        RLock lock = RedissonLockUtil.getOrderLock(merchNo, orderNo);
//        if (lock.tryLock()) {
            try {
                Order order = RedisUtil.getOrder(merchNo, orderNo);
                if (order == null) {

                    logger.info("支付订单数据保存失败，订单不存在，{}，{}", merchNo, orderNo);
                    return;
                }
                Integer orderState = order.getOrderState();
                if (orderState == OrderState.succ.id() || orderState == OrderState.fail.id()
                        || orderState == OrderState.close.id()) {
                    boolean saveFlag = false;
//                    if (OutChannel.jfDesc().containsKey(order.getOutChannel())) {
//                        saveFlag = payQrService.saveQrOrderData(order);
//                    } else {
                    saveFlag = this.saveOrderData(order);
//                    }

                    if (saveFlag) {
                        RedisUtil.removeOrder(merchNo, orderNo);
                        logger.info("支付订单数据保存成功，{}，{}", merchNo, orderNo);
//                        lock.unlock();
                        RedisUtil.delKeyEventExpired(RedisConstants.cache_keyevent_ord, merchNo, orderNo);
                    }
                } else {
                    logger.info("支付订单数据保存失败，订单状态'{}'不满足条件，{}，{}", orderState, merchNo, orderNo);
                }
            } finally {
//                if (lock.isHeldByCurrentThread()) {
//                    lock.unlock();
//                }
            }

//        } else {
//            logger.info("支付订单数据保存失败，未获取到锁，不能保存，{}，{}", merchNo, orderNo);
//        }
    }


    @Override
    public void orderDataMsg(String outChannel, String accountNo, String monAmount,
                             String msg, String businessNo) {
//        String businessNo = UUID.randomUUID().toString().replaceAll("-", "");
        String chargeMerchNo = RedisUtil.getMonAmountOrderNo(outChannel, accountNo);
        String merchNo = chargeMerchNo.split(RedisConstants.link_symbol)[0];
        String orderNo = chargeMerchNo.split(RedisConstants.link_symbol)[1];
        RLock lock = RedissonLockUtil.getOrderLock(merchNo, orderNo);
        if (lock.tryLock()) {
            try {
                Order order = RedisUtil.getOrder(merchNo, orderNo);
                if (order == null) {
                    logger.info("支付订单数据保存失败，订单不存在，{}，{}", merchNo, orderNo);
                    return;
                }
                Integer orderState = order.getOrderState();
                if (orderState == OrderState.succ.id() || orderState == OrderState.fail.id()
                        || orderState == OrderState.close.id()) {
                    boolean saveFlag = false;
//                    if (OutChannel.jfDesc().containsKey(order.getOutChannel())) {
//                        saveFlag = payQrService.saveQrOrderData(order);
//                    } else {
                    saveFlag = this.saveOrderData(order);
//                    }

                    if (saveFlag) {
                        RedisUtil.removeOrder(merchNo, orderNo);
                        logger.info("支付订单数据保存成功，{}，{}", merchNo, orderNo);
                        lock.unlock();
                        RedisUtil.delKeyEventExpired(RedisConstants.cache_keyevent_ord, merchNo, orderNo);
                    }
                } else {
                    logger.info("支付订单数据保存失败，订单状态'{}'不满足条件，{}，{}", orderState, merchNo, orderNo);
                }
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }

        } else {
            logger.info("支付订单数据保存失败，未获取到锁，不能保存条件，{}，{}", merchNo, orderNo);
        }
    }

    /**
     * @param outChannel
     * @param monAmount
     * @param businessNo
     * @param msg
     * @Description 保存掉单数据
     */
    private void saveQrOrderLoseData(String outChannel, String accountNo, String monAmount,
                                     String businessNo,
                                     String msg) {
        Order order = payOrderLoseDao.getByBusinessNo(businessNo, outChannel);
        if (order != null) {
            return;
        }
        order = new Order();
        order.setOrderNo("0");
        order.setOutChannel(outChannel);
        order.setAmount(new BigDecimal(monAmount));
        order.setQhAmount(BigDecimal.ZERO);
        order.setBusinessNo(businessNo);
        order.setMerchNo(accountNo);
        if (ParamUtil.isNotEmpty(msg) && msg.length() > 50) {
            order.setMsg(msg.substring(0, 50));
        } else {
            order.setMsg(msg);
        }
        order.setOrderState(OrderState.init.id());
        order.setOrderType(OrderType.pay.id());
        order.setCrtDate(DateUtil.getCurrentTimeInt());
        order.setPayMerch(accountNo);
        payOrderLoseDao.save(order);
    }

    /**
     * @param outChannel
     * @param monAmount
     * @param businessNo
     * @Description 保存支付数据
     */
    private void saveQrOrderQrData(String outChannel, String accountNo, String monAmount,
                                   String businessNo, String orderNo) {
        Order order = payOrderQrDao.getByBusinessNo(businessNo);
        if (order != null) {
            return;
        }
        order = new Order();
        order.setOrderNo(orderNo);
        order.setOutChannel(outChannel);
        order.setMobile(accountNo);
        order.setAmount(new BigDecimal(monAmount));
        order.setBusinessNo(businessNo);
        order.setCrtDate(DateUtil.getCurrentTimeInt());
        payOrderQrDao.save(order);
    }

    @Autowired
    PayQrConfigService payQrConfigService;

    /**
     * @param order
     * @Description 保存订单数据
     */
    @Transactional
    boolean saveOrderData(Order order) {
        // 商户信息
        Merchant merchant = merchantService.get(order.getMerchNo());
//        Order orderqr = payOrderQrDao.getByBusinessNo(order.getBusinessNo());
//        PayQrConfigDO payQrConfig = payQrConfigService.get(order.getOutChannel(), orderqr.getMobile());

        // 支付通道信息
//        PayConfigCompanyDO payCfgComp = payCfgCompService.get(order.getPayCompany(), order.getPayMerch(),
//                order.getOutChannel());
        BigDecimal amount = order.getAmount();
        //费率单位
//        Integer costRateUnit = payCfgComp.getCostRateUnit();
        // 成本金额
//        if (payCfgComp.getCostRate() != null) {
//            if (costRateUnit.equals(PaymentRateUnit.PRECENT.id())) {
//                order.setCostAmount(ParamUtil.multBig(amount, payCfgComp.getCostRate().divide(new BigDecimal(100))));
//            } else if (costRateUnit.equals(PaymentRateUnit.YUAN.id())) {
//                order.setCostAmount(payCfgComp.getCostRate());
//            }
//        } else {

        order.setCostAmount(BigDecimal.ZERO);
//        }

        // 商户金额
        BigDecimal jfRate = null;
//        Integer jfUnit = null;
        String outChannel = order.getOutChannel();
        //代理商收取商户费率
        Map<String, BigDecimal> rateMap = merchant.getCoinRate().get(outChannel);
        jfRate = new BigDecimal(String.valueOf(rateMap.get(PayConstants.PAYMENT_RATE)));
//        jfUnit = Integer.valueOf(rateMap.get(PayConstants.PAYMENT_UNIT).toString());
        order.setPaymentMethod(PaymentMethod.D0.id());

        BigDecimal agentAmount = BigDecimal.ZERO;
        if (jfRate != null) {
//            if (jfUnit.equals(PaymentRateUnit.PRECENT.id())) {
            jfRate = jfRate.divide(new BigDecimal(100));
            agentAmount = ParamUtil.multBig(amount, jfRate);
//            } else if (jfUnit.equals(PaymentRateUnit.YUAN.id())) {
//                qhAmount = jfRate;
//            }
        }


        // 代理信息
        Agent agent = agentService.get(merchant.getParentAgent());
        rateMap = agent.getCoinRate().get(outChannel);
        //平台收取商户费率

        jfRate = new BigDecimal(String.valueOf(rateMap.get(PayConstants.PAYMENT_RATE)));
//        jfUnit = Integer.valueOf(rateMap.get(PayConstants.PAYMENT_UNIT).toString());
        BigDecimal qhAmount = BigDecimal.ZERO;

        if (jfRate != null) {
//            if (jfUnit.equals(PaymentRateUnit.PRECENT.id())) {
            jfRate = jfRate.divide(new BigDecimal(100));
            qhAmount = amount.multiply(jfRate);//ParamUtil.multSmall(amount, jfRate);   不做舍去,否则在后续算手续费时可能给代理多算
            // 如：654行
//            } else if (jfUnit.equals(PaymentRateUnit.YUAN.id())) {
//                agentAmount = jfRate;
//            }
        }
        if (qhAmount.add(agentAmount).compareTo(new BigDecimal(0.1)) < 0) {
            agentAmount = new BigDecimal(0.05);
            qhAmount = new BigDecimal(0.05);
        }


        //代理金额
        order.setAgentAmount(agentAmount);
        //平台金额
        order.setCostAmount(qhAmount);

//        String parentAgentNumber = agent.getParentAgent();
        //上级代理
//        if (agent.getLevel() == AgentLevel.two.id()) {
//            order.setSubAgentAmount(ParamUtil.subSmall(order.getQhAmount(), agentAmount));//qhAmount=3.51
//            // agentAmount=2.6499   在644行：不舍去99等于0.8601  舍去99等于0.87
//            Agent paramAgent = agentService.get(parentAgentNumber);
//            rateMap = paramAgent.getCoinRate().get(outChannel);
//            jfRate = new BigDecimal(rateMap.get(PayConstants.PAYMENT_RATE).toString());
//            jfUnit = Integer.valueOf(rateMap.get(PayConstants.PAYMENT_UNIT).toString());
//			if (ParamUtil.isNotEmpty(merchant.getFeeRate())) {
//				feeRate = merchant.getFeeRate().get(order.getOutChannel());
//			}
//            if (jfRate != null) {
//                BigDecimal parentAgentAmount = BigDecimal.ZERO;
//                if (jfUnit.equals(PaymentRateUnit.PRECENT.id())) {
//                    jfRate = jfRate.divide(new BigDecimal(100));
//                    parentAgentAmount = amount.multiply(jfRate);//ParamUtil.multSmall(amount, jfRate);  不做舍去,
//                    // 否则在后续算手续费时可能给代理多算
//                } else if (jfUnit.equals(PaymentRateUnit.YUAN.id())) {
//                    parentAgentAmount = jfRate;
//                }
//                order.setAgentAmount(ParamUtil.subSmall(agentAmount, parentAgentAmount));
//                agentAmount = parentAgentAmount;
//            }
//        } else {
        //代理价格
//        order.setAgentAmount(ParamUtil.subSmall(order.getQhAmount(), agentAmount));
//        }


        int crtDate = order.getCrtDate();
        if (ParamUtil.isNotEmpty(order.getMsg()) && order.getMsg().length() > 50) {
            order.setMsg(order.getMsg().substring(0, 50));
        }

        order.setClearState(ClearState.succ.id());
        payOrderDao.save(order);
        int orderState = order.getOrderState();
        if (orderState != OrderState.succ.id()) {
            return true;
        }
        //日限量
        String key = CfgKeyConst.MERCHANT_DAY_LIMIT + order.getOutChannel() + order.getPayMerch();
        Object value = RedisUtil.getValue(key);
        if (value != null) {
            BigDecimal amountDay = (BigDecimal) value;
            amountDay = amountDay.add(order.getAmount());
            RedisUtil.setValue(key, amountDay);
            RedisUtil.getRedisTemplate().expire(key, DateUtil.getDayLeftSeconds(), TimeUnit.SECONDS);
        }
//        key = CfgKeyConst.MERCHANT_MONTH_LIMIT+order.getOutChannel() + order.getMerchNo();
//        value = null;
//        value = RedisUtil.getValue(key);
//        if (value != null) {
//            BigDecimal amountMonth = (BigDecimal) value;
//            amountMonth = amountMonth.add(order.getAmount());
//            RedisUtil.setValue(key, amountMonth);
//            RedisUtil.getRedisTemplate().expire(key, DateUtil.getMonthLeftSeconds(), TimeUnit.SECONDS);
//        }

        // 增加商户余额以及流水
        RecordMerchBalDO rdMerchBal = payHandlerService.balForMerchAdd(order, order.getAmount(),
                FeeType.merchIn.id(), OrderType.pay.id());
        rdMerchBal.setCrtDate(crtDate);
        rdMerchBalDao.save(rdMerchBal);
        //减少商户余额（手续费）
        rdMerchBal = payHandlerService.balForMerchSub(order, order.getAgentAmount().add(order.getCostAmount()),
                FeeType.merchHandFee.id(), OrderType.pay.id());
        rdMerchBal.setCrtDate(crtDate);
        rdMerchBalDao.save(rdMerchBal);

        RecordFoundAcctDO rdFoundAcct = null;
        // 增加代理余额以及流水
//        if (agent.getLevel() == AgentLevel.two.id()) {
//            rdFoundAcct = payHandlerService.balForAgentAdd(order, order.getSubAgentAmount(), agent.getAgentNumber(),
//                    FeeType.agentIn.id(), OrderType.pay.id());
//            rdFoundAcct.setCrtDate(crtDate);
//            rdFoundAcctDao.save(rdFoundAcct);
//
//            // 增加上级代理余额以及流水
//            rdFoundAcct = payHandlerService.balForAgentAdd(order, order.getAgentAmount(), parentAgentNumber,
//                    FeeType.agentIn.id(), OrderType.pay.id());
//            rdFoundAcct.setCrtDate(crtDate);
//            rdFoundAcctDao.save(rdFoundAcct);
//
//        } else {
        //增加代理余额以及流水
        rdFoundAcct = payHandlerService.balForAgentAdd(order, order.getAgentAmount(), agent.getAgentNumber(),
                FeeType.agentIn.id(), OrderType.pay.id());
        rdFoundAcct.setCrtDate(crtDate);
        rdFoundAcctDao.save(rdFoundAcct);
//        }
//        BigDecimal subAgentAmount = order.getSubAgentAmount();
//        subAgentAmount = subAgentAmount == null ? BigDecimal.ZERO : subAgentAmount;
//        BigDecimal platMoney =
//                order.getQhAmount().subtract(order.getCostAmount().add(order.getAgentAmount()));
////                order.getQhAmount().subtract(order.getCostAmount().add(order.getAgentAmount()).add(subAgentAmount));
//        platMoney = platMoney.compareTo(BigDecimal.ZERO) == -1 ? BigDecimal.ZERO : platMoney;
        // 增加平台资金账户余额以及流水
        rdFoundAcct = payHandlerService.balForPlatAdd(order, order.getCostAmount(),
                FeeType.platIn.id(), OrderType.pay.id());
        rdFoundAcct.setCrtDate(crtDate);
        rdFoundAcctDao.save(rdFoundAcct);

        // 增加第三方支付公司资金账户余额以及流水
//        RecordPayMerchBalDO rdPayMerchAcct = payHandlerService.balForPayMerchAdd(order, order.getAmount(),
//                FeeType.payMerchTrade.id(), OrderType.pay.id());
//        rdPayMerchAcct.setCrtDate(crtDate);
//        rdPayMerchBalDao.save(rdPayMerchAcct);

        // 减去第三方支付公司资金账户余额以及流水    减手续费
//        rdPayMerchAcct = payHandlerService.balForPayMerchSub(order, order.getCostAmount(),
//                FeeType.payMerchTradeHand.id(), OrderType.pay.id());
//        rdPayMerchAcct.setCrtDate(crtDate);
//        rdPayMerchBalDao.save(rdPayMerchAcct);

        try {
//				dZeroSettlement(order, agent, parentAgentNumber, crtDate, ptAmount);
            List<Order> updateOrders = new ArrayList<Order>();
            order.setClearState(ClearState.succ.id());
            updateOrders.add(order);
            if (payHandlerService.updateClearStateBatch(updateOrders) > 0) {
                payHandlerService.availBalForOrderClearSucc(updateOrders);
            }
        } catch (Exception e) {
            logger.debug("D0清算异常(保存订单数据)");
            e.printStackTrace();
        }

        //记录支付公司单个用户的当日交易金额
        JSONObject useCapitalPoolJo =
                (JSONObject) RedisUtil.getRedisTemplate().opsForHash().get(RedisConstants.CACHE_COMPANY_MERCHANT_CAPITAL_POOL + order.getOutChannel(), order.getPayMerch());
        if (useCapitalPoolJo != null) {
            BigDecimal curMoney = useCapitalPoolJo.getBigDecimal(RedisConstants.COMPANY_MERCHANT_CUR_MONEY);
            curMoney = curMoney == null ? new BigDecimal(0) : curMoney;
            curMoney = curMoney.add(order.getAmount());
            useCapitalPoolJo.put(RedisConstants.COMPANY_MERCHANT_CUR_MONEY, curMoney);
            RedisUtil.getRedisTemplate().opsForHash().put(RedisConstants.CACHE_COMPANY_MERCHANT_CAPITAL_POOL + order.getOutChannel(), order.getPayMerch(), useCapitalPoolJo);
        }
        return true;
    }

    /**
     * D0 清算
     *
     * @param order
     * @param agent
     * @param parentAgentNumber
     * @param crtDate
     * @param ptAmount
     */
    @Transactional
    void dZeroSettlement(Order order, Agent agent, String parentAgentNumber, int crtDate,
                         BigDecimal ptAmount) {
        RecordMerchBalDO rdMerchBal = null;
        RecordFoundAcctDO rdFoundAcct = null;
        //D0结算,实时结算
        // 增加商户可用余额以及流水
        rdMerchBal = payHandlerService.availBalForMerchAdd(order, order.getAmount(),
                FeeType.merchIn.id(), OrderType.pay.id());
        rdMerchBal.setCrtDate(crtDate);
        rdMerchAvailBalDao.save(rdMerchBal);
        // 减少商户可用余额以及流水 手续费
        rdMerchBal = payHandlerService.availBalForMerchSub(order, order.getQhAmount(),
                FeeType.merchHandFee.id(), OrderType.pay.id());
        rdMerchBal.setCrtDate(crtDate);
        rdMerchAvailBalDao.save(rdMerchBal);

        if (agent.getLevel() == AgentLevel.two.id()) {
            rdFoundAcct = payHandlerService.availBalForAgentAdd(order,
                    order.getQhAmount().subtract(order.getSubAgentAmount()), agent.getAgentNumber(),
                    FeeType.agentIn.id(), OrderType.pay.id());
            rdFoundAcct.setCrtDate(crtDate);
            rdFoundAvailAcctDao.save(rdFoundAcct);

            // 增加上级代理余额以及流水
            rdFoundAcct = payHandlerService.availBalForAgentAdd(order,
                    order.getSubAgentAmount().subtract(order.getAgentAmount()), parentAgentNumber,
                    FeeType.agentIn.id(), OrderType.pay.id());
            rdFoundAcct.setCrtDate(crtDate);
            rdFoundAvailAcctDao.save(rdFoundAcct);
            ptAmount = order.getAgentAmount();
        } else {
            rdFoundAcct = payHandlerService.availBalForAgentAdd(order,
                    order.getQhAmount().subtract(order.getAgentAmount()), agent.getAgentNumber(),
                    FeeType.agentIn.id()
                    , OrderType.pay.id());
            rdFoundAcct.setCrtDate(crtDate);
            rdFoundAvailAcctDao.save(rdFoundAcct);
        }
        ptAmount = order.getAgentAmount();
        // 增加平台资金账户可用余额以及流水
        rdFoundAcct = payHandlerService.availBalForPlatAdd(order,
                ptAmount.subtract(order.getCostAmount()),
                FeeType.platIn.id(), OrderType.pay.id());
        rdFoundAcct.setCrtDate(crtDate);
        rdFoundAvailAcctDao.save(rdFoundAcct);

        // 增加第三方支付公司资金账户可用余额以及流水
        RecordPayMerchBalDO rdPayMerchAcct = payHandlerService.availBalForPayMerchAdd(order, order.getAmount(),
                FeeType.payMerchTrade.id(), OrderType.pay.id());
        rdPayMerchAcct.setCrtDate(crtDate);
        rdPayMerchAvailBalDao.save(rdPayMerchAcct);

        // 减去第三方支付公司资金账户可用余额以及流水    减手续费
        rdPayMerchAcct = payHandlerService.availBalForPayMerchSub(order, order.getCostAmount(),
                FeeType.payMerchTradeHand.id(), OrderType.pay.id());
        rdPayMerchAcct.setCrtDate(crtDate);
        rdPayMerchAvailBalDao.save(rdPayMerchAcct);

    }

    /*
     * (非 Javadoc) Description:
     *
     * @see com.qh.pay.service.PayService#query(com.qh.pay.domain.Merchant,
     * com.alibaba.fastjson.JSONObject)
     */
    @Override
    public R query(Merchant merchant, JSONObject jo) {
        String orderNo = jo.getString(OrderParamKey.orderNo.name());
        if (ParamUtil.isEmpty(orderNo)) {
            return R.error("查询支付订单号为空！");
        }
//        RLock lock = RedissonLockUtil.getOrderLock(merchant.getMerchNo() + RedisConstants.link_symbol + orderNo);
//        if (lock.tryLock()) {
            Order order = null;
            String msg = Constant.result_msg_succ;
            try {
                order = RedisUtil.getOrder(merchant.getMerchNo(), orderNo);
                boolean dataFlag = false;
                if (order == null) {
                    order = payOrderDao.get(orderNo, merchant.getMerchNo());
                    dataFlag = true;
                }
                if (order == null) {
                    return R.error(merchant.getMerchNo() + "," + orderNo + "支付订单不存在！");
                }

                // 无支付成功结果去第三方查询
                if (!dataFlag && OrderState.succ.id() != order.getOrderState()) {
                    if (pay(order)) {
                        //支付成功
                        payQrService.notifyQr(order);
                    } else {
                        return R.error("订单未支付");
                    }
                    return R.error("订单未支付");
                }
            } finally {
//                if (lock.isHeldByCurrentThread()) {
//                    lock.unlock();
//                }
            }
            Map<String, String> data = PayService.initRspData(order);
            data.put(OrderParamKey.orderState.name(), String.valueOf(OrderState.succ.id() ));
            data.put(OrderParamKey.amount.name(), String.valueOf(order.getAmount()));
            data.put(OrderParamKey.orderNo.name(), order.getOrderNo());
            return decryptAndSign(jo.getString(OrderParamKey.encryptType.name()), data, merchant.getPublicKey());
//        } else {
//            return R.error("查询过于繁忙，请稍后再试！");
//        }


    }

    @Override
    public R query(String merchNo, String orderNo) {

        if (ParamUtil.isEmpty(orderNo)) {
            return R.error("查询支付订单号为空！");
        }
//        RLock lock = RedissonLockUtil.getOrderLock(merchNo + RedisConstants.link_symbol + orderNo);
//        if (lock.tryLock()) {
            Order order = null;
            String msg = Constant.result_msg_succ;
            try {
                order = RedisUtil.getOrder(merchNo, orderNo);
                boolean dataFlag = false;
                if (order == null) {
                    order = payOrderDao.get(orderNo, merchNo);
                    dataFlag = true;
                }
                if (order == null) {
                    return R.error(merchNo + "," + orderNo + "支付订单不存在！");
                }
                if (!dataFlag && OrderState.succ.id() != order.getOrderState()) {
                    if (pay(order)) {
                        //支付成功
                        payQrService.notifyQr(order);
                    } else {
                        return R.error("订单未支付");
                    }

                }
            } finally {
//                if (lock.isHeldByCurrentThread()) {
//                    lock.unlock();
//                }
            }
            return R.ok();
//        } else {
//            return R.error("查询过于繁忙，请稍后再试！");
//        }
    }


    private boolean pay(Order order) {
        PayQrConfigDO payQrConfigDO = payQrConfigService.get(order.getOutChannel(), order.getPayMerch());

        String resutl = null;
        try {
            resutl = Request.Get("http://api.tbk.dingdanxia.com/shop/order?apikey=" + PayApi.apikey+
                    "&nick="+payQrConfigDO.getAccountName()+"&order_no="+payQrConfigDO.getAccountNo())
                    .execute().returnContent().asString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (ParamUtil.isNotEmpty(resutl)) {
            JSONObject jsonObject = JSONObject.parseObject(resutl).getJSONObject("data");
            if ("TRADE_FINISHED".equals(jsonObject.getString("status")) || "SELLER_CONSIGNED_PART".equals(jsonObject.getString(
                    "status")) || "WAIT_SELLER_SEND_GOODS".equals(jsonObject.getString("status"))
                    || "WAIT_BUYER_CONFIRM_GOODS".equals(jsonObject.getString("status"))) {
//                payQrConfigService.remove(payQrConfigDO.getId().toString());
                payQrConfigService.updateQrPayState("1",payQrConfigDO.getAccountNo(),payQrConfigDO.getOutChannel());

                return true;
            }

        }

        return false;
    }

    /**
     * @param data
     * @return
     * @Description 公钥加密，私钥签名
     */
    private R decryptAndSign(String encryptType, Map<String, ?> data, String publicKey) {
        if ("RSA".equals(encryptType)) {
            return decryptAndSign(data, publicKey, "");
        } else if ("MD5".equals(encryptType)) {
            return decryptAndSignMD5(data, "");
        } else {
            return decryptAndSign(data, publicKey, "");
        }

    }

    /**
     * @param data
     * @return
     * @Description 公钥加密，私钥签名
     */
    private R decryptAndSign(Map<String, ?> data, String publicKey, String msg) {
        try {
            logger.info("回调商户 返回明文数据:" + JSON.toJSONString(data));
            String merchNo = data.get("merchNo").toString();
            R r = R.ok();
            byte[] context = RSAUtil.encryptByPublicKey(JSON.toJSONBytes(data), publicKey);
            String privateKey = merchantService.getPrivateKey(merchNo);
            String sign = RSAUtil.sign(context, privateKey);
            r.put("sign", sign).put("context", context);
            r.put(Constant.result_msg, msg).put("merchNo", merchNo).put(OrderParamKey.encryptType.name(), "RSA");
            logger.info("回调商户 返回加密数据:" + JSON.toJSONString(r));
            return r;
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("返回数据签名 失败！");
        }
        return R.error("返回数据签名失败！");
    }

//    public static void main(String[] args) {
//        Map<String, String> data = new HashMap<>();
//        data.put(OrderParamKey.merchNo.name(),"SH228704");
//        data.put(OrderParamKey.orderNo.name(), "");
//        data.put(OrderParamKey.outChannel.name(),"ali");
//        data.put(OrderParamKey.orderState.name(), String.valueOf(1));
//        data.put(OrderParamKey.amount.name(), "1");
//      String  result = RequestUtils.doPostJson("http://testpay.testcg
//      .com/mSLJ/Accept/9B10424EA5547B524D3AECC367C2A6D10F9A83D6CC049D99FCF2CD4612EB82DB_",
//                decryptAndSignMD5(data, "成功").jsonStr());
//        logger.info("{}状态返回结果：{}", result);
//    }

    /**
     * @param data
     * @return
     * @Description 公钥加密，私钥签名
     */
    private R decryptAndSignMD5(Map<String, ?> data, String msg) {
        try {
            logger.info("回调商户 返回明文数据:" + JSON.toJSONString(data));
//            byte[]    context = JSON.toJSONString(data).getBytes("UTF-8");
            Base64.Encoder encoder = Base64.getEncoder();
            //编码
            String encodedText = encoder.encodeToString(JSON.toJSONString(data).getBytes("UTF-8"));
            String merchNo = data.get("merchNo").toString();
            R r = R.ok();

            String privateKey = merchantService.getPrivateKey(merchNo);
            String sign = Md5Util.sign(encodedText, privateKey, "UTF-8");
            r.put("sign", sign).put("context", encodedText);
            r.put(Constant.result_msg, msg).put("merchNo", merchNo).put(OrderParamKey.encryptType.name(), "MD5");
            logger.info("回调商户 返回加密数据:" + JSON.toJSONString(r));
            return r;
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("返回数据签名 失败！");
        }
        return R.error("返回数据签名失败！");
    }


    /*
     * (非 Javadoc) Description:
     *
     * @see com.qh.pay.service.PayService#orderAcp(com.qh.pay.domain.Merchant,
     * com.alibaba.fastjson.JSONObject)
     */
    @Override
    public R orderAcp(Merchant merchant, JSONObject jo) {
        String merchNo = merchant.getMerchNo();
        String orderNo = jo.getString(OrderParamKey.orderNo.name());
        RLock lock = RedissonLockUtil.getOrderAcpLock(merchNo + RedisConstants.link_symbol + orderNo);
        if (lock.tryLock()) {
            try {
                Order order = new Order();
                // 初始化订单信息
//                order.setOutChannel(OutChannel.acp.name());
                String initResult = payHandlerService.initOrder(order, jo);
                order.setOrderType(OrderType.acp.id());
                if (ParamUtil.isNotEmpty(initResult)) {
                    logger.error(initResult);
                    return R.error(initResult);
                }
                //userId不能为商户号
                if (ParamUtil.isNotEmpty(order.getUserId()) && merchNo.equals(order.getUserId())) {
                    return R.error("userId不能与商户号一致");
                }
                if (RedisUtil.getOrderAcp(merchNo, orderNo) != null) {
                    logger.error(merchNo + "," + orderNo + "订单号已经存在！");
                    return R.error(merchNo + "," + orderNo + "订单号已经存在！");
                } else if (payOrderAcpDao.get(orderNo, merchNo) != null) {
                    logger.error(merchNo + "," + orderNo + "订单号已经存在！");
                    return R.error(merchNo + "," + orderNo + "订单号已经存在！");
                }

                BigDecimal amount = order.getAmount();
                //计算出  商户代付  手续费费率
//				Map<String,?> paidMap = merchant.getCoinRate().get(OutChannel.acp.name());
//				BigDecimal jfRate = new BigDecimal(paidMap.get(PayConstants.PAYMENT_RATE).toString());
//				Integer jfUnit = Integer.valueOf(paidMap.get(PayConstants.PAYMENT_UNIT).toString());
//				if(jfRate != null){
//					if(jfUnit.equals(PaymentRateUnit.PRECENT.id())) {
//						jfRate = jfRate.divide(new BigDecimal(100));
//						order.setQhAmount(ParamUtil.multBig(amount, jfRate));
//					}else if(jfUnit.equals(PaymentRateUnit.YUAN.id())) {
//						order.setQhAmount(jfRate);
//					}
//				}

                amount = amount.add(order.getQhAmount());

                List<RecordMerchBalDO> rdMerchAvailBalList = new ArrayList<RecordMerchBalDO>();
                RecordMerchBalDO rdMerchAvailBal = PayService.initRdMerchBal(order, FeeType.merchAcpOut.id(),
                        OrderType.acp.id(), ProfitLoss.loss.id());
                rdMerchAvailBal.setCrtDate(DateUtil.getCurrentTimeInt());
                // 检查商户余额 -----》商户可用余额
                RLock merchLock = RedissonLockUtil.getBalMerchLock(merchNo);
                try {
                    merchLock.lock();
                    PayAcctBal pab = RedisUtil.getMerchBal(merchNo);
                    if (pab == null || pab.getAvailBal().compareTo(amount) < 0) {
                        return R.error("商户号 " + merchNo + ",可用余额不足！");
                    }
                    R r = checkAcpCfgComp(order);
                    if (R.ifError(r)) {
                        return r;
                    }


                    rdMerchAvailBal.setTranAmt(order.getAmount());
                    payHandlerService.availBalForMerchSub(order, rdMerchAvailBal, pab);
                    RedisUtil.setMerchBal(pab);
                    rdMerchAvailBalList.add(rdMerchAvailBal);

                    //扣除商户代付手续费
                    rdMerchAvailBal = PayService.initRdMerchBal(order, FeeType.merchAcpHandFee.id(),
                            OrderType.acp.id(), ProfitLoss.loss.id());
                    rdMerchAvailBal.setCrtDate(DateUtil.getCurrentTimeInt());
                    rdMerchAvailBal.setTranAmt(order.getQhAmount());
                    payHandlerService.availBalForMerchSub(order, rdMerchAvailBal, pab);
                    RedisUtil.setMerchBal(pab);

                    rdMerchAvailBalList.add(rdMerchAvailBal);
                } finally {
                    merchLock.unlock();
                }


                PayAuditDO payAudit = PayService.initPayAudit(order, AuditType.order_acp.id());
                int count = 0;
                try {
                    payAudit.setCrtTime(rdMerchAvailBal.getCrtDate());
                    count = payAuditDao.save(payAudit);
                } catch (Exception e) {
                    count = 0;
                }
                try {
                    //保存可用余额流水
                    rdMerchAvailBalDao.saveBatch(rdMerchAvailBalList);
                } catch (Exception e) {
                    logger.error("代付保存可用余额流水记录失败！{}，{}", merchNo, orderNo);
                }
                if (count == 1) {
                    RedisUtil.setOrderAcp(order);
                    //如果成功，商户余额减少
                    try {
                        rdMerchBalDao.save(payHandlerService.balForMerchSub(order, order.getAmount(),
                                FeeType.merchAcpOut.id(), OrderType.acp.id()));
                        rdMerchBalDao.save(payHandlerService.balForMerchSub(order, order.getQhAmount(),
                                FeeType.merchAcpHandFee.id(), OrderType.acp.id()));
                    } catch (Exception e) {
                        logger.error("代付保存余额流水记录失败！{}，{}", merchNo, orderNo);
                    }

                    //自动审核通过
                    String pollMoneyValue = RedisUtil.getConfigValue(CfgKeyConst.PAY_AUDIT_AUTO_ACP,
                            ConfigParent.payAuditConfig.name());
                    if (pollMoneyValue != null && YesNoType.yes.id() == Integer.parseInt(pollMoneyValue)) {
//                        if (payAuditService.audit(orderNo, merchNo, AuditType.order_acp.id(), AuditResult.pass.id(),
//                                null) > 0) {
//                            lock.unlock();
//                            RedisMsg.orderAcpMsg(merchNo, orderNo);
//                        }
                    }

                    Map<String, String> data = PayService.initRspData(order);
                    data.put(OrderParamKey.orderState.name(), String.valueOf(OrderState.init.id()));
                    return decryptAndSign(data, merchant.getPublicKey(), "代付订单已受理!");
                } else {// 出错了返还可用余额
                    rdMerchAvailBal = payHandlerService.availBalForMerchAdd(order, order.getAmount(),
                            FeeType.merchAcpFail.id(), OrderType.acp.id());
                    rdMerchAvailBal.setCrtDate(payAudit.getCrtTime());
                    rdMerchAvailBalDao.save(rdMerchAvailBal);

                    //返回手续费
                    rdMerchAvailBal = payHandlerService.availBalForMerchAdd(order, order.getQhAmount(),
                            FeeType.merchAcpHandFee.id(), OrderType.acp.id());
                    rdMerchAvailBal.setCrtDate(payAudit.getCrtTime());
                    rdMerchAvailBalDao.save(rdMerchAvailBal);

                    return R.error("代付异常！");
                }
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            return R.error(merchNo + "," + orderNo + "下单失败！");
        }
    }

    /*
     * (非 Javadoc) Description: 审核未通过 可用余额/余额返还
     *
     * @see
     * com.qh.pay.service.PayService#orderAcpNopassDataMsg(java.lang.String,java.lang.String)
     */
    @Override
    public void orderAcpNopassDataMsg(String merchNo, String orderNo) {
        RLock lock = RedissonLockUtil.getOrderAcpLock(merchNo, orderNo);
        if (lock.tryLock()) {
            try {
                Order order = RedisUtil.getOrderAcp(merchNo, orderNo);
                if (order != null) {
                    //提现与代付区别
                    Integer orderType = order.getOrderType();
                    Integer feeType = FeeType.merchAcpFail.id();
                    if (orderType == null) {
                        orderType = OrderType.acp.id();
                    }
                    if (orderType == OrderType.withdraw.id()) {
                        feeType = FeeType.withdrawFail.id();
                    }
                    Integer userType = order.getUserType();
                    if (userType != null && (UserType.agent.id() == userType || UserType.subAgent.id() == userType)) {
                        List<RecordFoundAcctDO> rdFoundAvailBalList = new ArrayList<RecordFoundAcctDO>();
                        List<RecordFoundAcctDO> rdFoundBalList = new ArrayList<RecordFoundAcctDO>();
                        int crtTime = DateUtil.getCurrentTimeInt();
                        RecordFoundAcctDO rdAgentAvailBal = payHandlerService.availBalForAgentAdd(order,
                                order.getAmount(), order.getMerchNo(), feeType, orderType);
                        rdAgentAvailBal.setCrtDate(crtTime);
                        rdFoundAvailBalList.add(rdAgentAvailBal);

                        rdAgentAvailBal = payHandlerService.availBalForAgentAdd(order, order.getQhAmount(),
                                order.getMerchNo(), FeeType.agentWithDrawHandFee.id(), orderType);
                        rdAgentAvailBal.setCrtDate(crtTime);
                        rdFoundAvailBalList.add(rdAgentAvailBal);

                        RecordFoundAcctDO rdAgentBal = payHandlerService.balForAgentAdd(order, order.getAmount(),
                                order.getMerchNo(), feeType, orderType);
                        rdAgentBal.setCrtDate(crtTime);
                        rdFoundBalList.add(rdAgentBal);
                        rdAgentBal = payHandlerService.balForAgentAdd(order, order.getQhAmount(),
                                order.getMerchNo(),
                                FeeType.agentWithDrawHandFee.id(), orderType);
                        rdAgentBal.setCrtDate(crtTime);
                        rdFoundBalList.add(rdAgentBal);

                        RedisUtil.removeOrderAcp(merchNo, orderNo);

                        rdFoundAvailAcctDao.saveBatch(rdFoundAvailBalList);
                        rdFoundAcctDao.save(rdFoundBalList.get(0));
                        rdFoundAcctDao.save(rdFoundBalList.get(1));


                    } else {
                        List<RecordMerchBalDO> rdMerchAvailBalList = new ArrayList<RecordMerchBalDO>();
                        List<RecordMerchBalDO> rdMerchBalList = new ArrayList<RecordMerchBalDO>();
                        //返还可用余额
                        RecordMerchBalDO rdMerchAvailBal = payHandlerService.availBalForMerchAdd(order,
                                order.getAmount(), feeType, orderType);
                        rdMerchAvailBalList.add(rdMerchAvailBal);
                        rdMerchAvailBal = payHandlerService.availBalForMerchAdd(order, order.getQhAmount(),
                                FeeType.merchWithDrawHandFee.id(), orderType);
                        rdMerchAvailBalList.add(rdMerchAvailBal);
                        //返还余额
                        RecordMerchBalDO rdMerchBal = payHandlerService.balForMerchAdd(order, order.getAmount(),
                                feeType, orderType);
                        rdMerchBalList.add(rdMerchBal);
                        rdMerchBal = payHandlerService.balForMerchAdd(order, order.getQhAmount(),
                                FeeType.merchWithDrawHandFee.id(), orderType);
                        rdMerchBalList.add(rdMerchBal);

                        RedisUtil.removeOrderAcp(merchNo, orderNo);

                        rdMerchAvailBalDao.saveBatch(rdMerchAvailBalList);
                        rdMerchBalDao.save(rdMerchBalList.get(0));
                        rdMerchBalDao.save(rdMerchBalList.get(1));
                    }

                    //如果是提现 不需要发送
                    if (orderType != OrderType.withdraw.id()) {
                        logger.error("发送代付审核不通过请求：{},{},{}", order.getNotifyUrl(), order.getMerchNo(),
                                order.getOrderNo());
                        RequestUtils.doPostJson(order.getNotifyUrl(), R.error(order.getMsg()).jsonStr());
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }

    /*
     * (非 Javadoc) Description:
     *
     * @see com.qh.pay.service.PayService#orderAcp(java.lang.String,java.lang.String)
     */
    @Override
    public R orderAcp(String merchNo, String orderNo) {
        RLock lock = RedissonLockUtil.getOrderAcpLock(merchNo, orderNo);
        if (lock.tryLock()) {
            try {
                Order order = RedisUtil.getOrderAcp(merchNo, orderNo);
                if (order == null) {
                    return R.error("代付订单不存在");
                }
                R r = payBaseService.orderAcp(order);
                order.setMsg((String) r.get(Constant.result_msg));
                if (R.ifError(r)) {
                    order.setOrderState(OrderState.fail.id());
                }
                RedisUtil.setOrderAcp(order);
                lock.unlock();
                //如果成功，并且订单状态成功，并且是实时返回结果则直接发起回调
                if (R.ifSucc(r) && Integer.valueOf(OrderState.succ.id()).equals(order.getOrderState()) &&
                        Integer.valueOf(YesNoType.yes.id()).equals(r.get(PayConstants.acp_real_time))) {
                    RedisMsg.orderAcpNotifyMsg(merchNo, orderNo);
                }
                if (Integer.valueOf(OrderState.fail.id()).equals(order.getOrderState())) {
                    r = R.error(order.getMsg());
                }
                return r;
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            logger.info("orderAcp，未获取到锁，{}，{}", merchNo, orderNo);
        }
        return null;
    }

    /*
     * (非 Javadoc) Description:
     *
     * @see com.qh.pay.service.PayService#orderAcpNotifyMsg(java.lang.String,java.lang.String)
     */
    @Override
    public String orderAcpNotifyMsg(String merchNo, String orderNo) {
        RLock lock = RedissonLockUtil.getOrderAcpLock(merchNo, orderNo);
        if (lock.tryLock()) {
            try {
                String result = orderAcpNotify(merchNo, orderNo);
                lock.unlock();
                return result;
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            logger.info("orderAcpNotifyMsg，未获取到锁，{}，{}", merchNo, orderNo);
        }
        return null;
    }

    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayService#eventOrderAcpNotifyMsg(java.lang.String, java.lang.String)
     */
    @Override
    public String eventOrderAcpNotifyMsg(String merchNo, String orderNo) {
        RLock lock = RedissonLockUtil.getEventOrderAcpLock(merchNo, orderNo);
        if (lock.tryLock()) {
            try {
                String result = orderAcpNotify(merchNo, orderNo);
                lock.unlock();
                return result;
            } finally {
                if (lock.isHeldByCurrentThread())
                    lock.unlock();
            }
        } else {
            logger.info("eventOrderAcpNotifyMsg，未获取到锁，{}，{}", merchNo, orderNo);
        }
        return null;
    }

    /**
     * @param merchNo
     * @param orderNo
     * @return
     * @Description 代付订单通知发起
     */
    private String orderAcpNotify(String merchNo, String orderNo) {
        Order order = RedisUtil.getOrderAcp(merchNo, orderNo);
        if (Integer.valueOf(OrderType.withdraw.id()).equals(order.getOrderType())) {
            return Constant.result_msg_ok;
        }
        return orderAcpNotify(order);
    }


    /**
     * @param order
     * @return
     * @Description 代付通知
     */
    private String orderAcpNotify(Order order, String notifyUrl) {
        if (order == null) {
            return null;
        }
        String stateDesc = OrderState.desc().get(order.getOrderState());
        String result = null;
        if (StringUtils.isBlank(notifyUrl)) {
            notifyUrl = order.getNotifyUrl();
        }
        logger.info("发送代付通知请求：{},{},{},{}", notifyUrl, order.getMerchNo(), order.getOrderNo(), stateDesc);
        if (OrderState.init.id() == order.getOrderState()) {
            //result = RequestUtils.doPostJson(order.getNotifyUrl(), R.error(order.getMsg()).jsonStr());
            logger.info("代付发起{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo());
        } else {
            Map<String, String> data = PayService.initRspData(order);
            data.put(OrderParamKey.orderState.name(), String.valueOf(order.getOrderState()));
            data.put(OrderParamKey.businessNo.name(), order.getBusinessNo());
            data.put(OrderParamKey.amount.name(), order.getRealAmount() == null ? order.getAmount().toString() :
                    order.getRealAmount().toString());
            Merchant merchant = merchantService.get(order.getMerchNo());

            result = RequestUtils.doPostJson(notifyUrl,
                    decryptAndSign(data, merchant.getPublicKey(), order.getMsg()).jsonStr());
            logger.info("代付发起{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo(), result);
            if (result.contains("ok") || result.contains("success")) {
                result = Constant.result_msg_ok;
            }
        }
        return result;
    }

    /**
     * @param order
     * @return
     * @Description 代付通知
     */
    private String orderAcpNotify(Order order) {
        if (order == null) {
            return null;
        }
        String stateDesc = OrderState.desc().get(order.getOrderState());
        String result = null;
        logger.info("发送代付通知请求：{},{},{},{}", order.getNotifyUrl(), order.getMerchNo(), order.getOrderNo(),
                stateDesc);
        if (OrderState.init.id() == order.getOrderState()) {
            //result = RequestUtils.doPostJson(order.getNotifyUrl(), R.error(order.getMsg()).jsonStr());
            logger.info("代付发起{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo());
        } else {
            Map<String, String> data = PayService.initRspData(order);
            data.put(OrderParamKey.orderState.name(), String.valueOf(order.getOrderState()));
            data.put(OrderParamKey.businessNo.name(), order.getBusinessNo());
            data.put(OrderParamKey.amount.name(), order.getRealAmount() == null ? order.getAmount().toString() :
                    order.getRealAmount().toString());
            Merchant merchant = merchantService.get(order.getMerchNo());
            result = RequestUtils.doPostJson(order.getNotifyUrl(),
                    decryptAndSign(data, merchant.getPublicKey(), order.getMsg()).jsonStr());
            logger.info("代付发起{}状态返回结果：{},{},{}", stateDesc, order.getMerchNo(), order.getOrderNo(), result);
            if (result.contains("ok") || result.contains("success")) {
                result = Constant.result_msg_ok;
            }
        }
        return result;
    }

    /*
     * (非 Javadoc) Description:
     *
     * @see com.qh.pay.service.PayService#orderAcpDataMsg(java.lang.String,java.lang.String)
     */
    @Override
    public void orderAcpDataMsg(String merchNo, String orderNo) {
        RLock lock = RedissonLockUtil.getOrderAcpLock(merchNo, orderNo);
        if (lock.tryLock()) {
            try {
                Order order = RedisUtil.getOrderAcp(merchNo, orderNo);
                if (order == null) {
                    logger.info("代付订单数据保存失败，订单不存在，{}，{}", merchNo, orderNo);
                    return;
                }
                Integer orderState = order.getOrderState();
                if (orderState == OrderState.succ.id() || orderState == OrderState.fail.id()
                        || orderState == OrderState.close.id()) {
                    Integer orderType = order.getOrderType();
                    order.setClearState(ClearState.succ.id());
                    if (OrderType.withdraw.id() == orderType) {
                        if (this.saveOrderWithdrawData(order)) {
                            RedisUtil.removeOrderAcp(merchNo, orderNo);
                            logger.info("提现订单数据保存成功，{}，{}", merchNo, orderNo);
                            lock.unlock();
                            RedisUtil.delKeyEventExpired(RedisConstants.cache_keyevent_acp, merchNo, orderNo);
                        }
                    } else {
                        if (this.saveOrderAcpData(order)) {
                            RedisUtil.removeOrderAcp(merchNo, orderNo);
                            logger.info("代付订单数据保存成功，{}，{}", merchNo, orderNo);
                            lock.unlock();
                            RedisUtil.delKeyEventExpired(RedisConstants.cache_keyevent_acp, merchNo, orderNo);
                        }
                    }
                } else {
                    logger.info("代付订单数据保存失败，订单状态'{}'不满足条件，{}，{}", orderState, merchNo, orderNo);
                }
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            logger.info("orderAcpDataMsg，未获取到锁，{}，{}", merchNo, orderNo);
        }

    }

    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayService#notifyAcp(java.lang.String, java.lang.String, javax.servlet.http
     * .HttpServletRequest, java.lang.String)
     */
    @Override
    public R notifyAcp(String merchNo, String orderNo, HttpServletRequest request, String requestBody) {
        RLock lock = RedissonLockUtil.getOrderAcpLock(merchNo + RedisConstants.link_symbol + orderNo);
        try {
            lock.lock();
            Order order = RedisUtil.getOrderAcp(merchNo, orderNo);
            if (order != null && (order.getLastLockTime() == null || DateUtil.getCurrentTimeInt() - order.getLastLockTime() > 20)) {
                R r = payBaseService.notifyAcp(order, request, requestBody);
                order.setMsg((String) r.get(Constant.result_msg));
                RedisUtil.setOrderAcp(order);
                RedisMsg.orderAcpNotifyMsg(merchNo, orderNo);
                return r;
            }
        } finally {
            lock.unlock();
        }
        return R.error("无效的结果");
    }


    /*
     * (非 Javadoc) Description:
     *
     * @see com.qh.pay.service.PayService#acpQuery(com.qh.pay.domain.Merchant,
     * com.alibaba.fastjson.JSONObject)
     */
    @Override
    public R acpQuery(Merchant merchant, JSONObject jo) {
        String orderNo = jo.getString(OrderParamKey.orderNo.name());
        if (ParamUtil.isEmpty(orderNo)) {
            return R.error("查询代付订单号为空！");
        }
        RLock lock = RedissonLockUtil.getOrderAcpLock(merchant.getMerchNo() + RedisConstants.link_symbol + orderNo);
        if (lock.tryLock()) {
            Order order = null;
            Integer orderState = null;
            String msg = null;
            try {
                order = RedisUtil.getOrderAcp(merchant.getMerchNo(), orderNo);
                boolean dataFlag = false;
                if (order == null) {
                    order = payOrderAcpDao.get(orderNo, merchant.getMerchNo());
                    dataFlag = true;
                }
                if (order == null) {
                    return R.error(merchant.getMerchNo() + "," + orderNo + "代付订单不存在！");
                }
                orderState = order.getOrderState();
                // 无支付结果去第三方查询
                if (!dataFlag && OrderState.succ.id() != order.getOrderState()) {
                    R r = payBaseService.acpQuery(order);
                    if (R.ifError(r)) {
                        return r;
                    }
                    msg = (String) r.get(Constant.result_msg);
                    orderState = order.getOrderState();
                    order.setMsg(msg);
                    RedisUtil.setOrderAcp(order);

                    if (OrderState.succ.id() == order.getOrderState()) {
                        if (payOrderAcpDao.get(orderNo, merchant.getMerchNo()) == null) {
                            lock.unlock();
                            RedisMsg.orderAcpDataMsg(merchant.getMerchNo(), orderNo);
                        } else {
                            logger.info("代付接口查询，订单已存在，不发通知保存.{},{}", merchant.getMerchNo(), orderNo);
                        }
                    }
                }
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
            Map<String, String> data = PayService.initRspData(order);
            data.put(OrderParamKey.orderState.name(), String.valueOf(orderState));
            data.put(OrderParamKey.businessNo.name(), order.getBusinessNo());
            data.put(OrderParamKey.amount.name(), order.getRealAmount().toString());
            return decryptAndSign(data, merchant.getPublicKey(), msg);
        } else {
            return R.error("查询过于繁忙，请稍后再试！");
        }
    }

    /**
     * @param order
     * @Description 保存提现订单数据
     */
    @Transactional
    boolean saveOrderWithdrawData(Order order) {

        Integer orderState = order.getOrderState();
        // 商户信息
//		Merchant merchant = merchantService.get(order.getMerchNo());
        // 支付通道信息
        PayConfigCompanyDO payCfgComp = payCfgCompService.get(order.getPayCompany(), order.getPayMerch(),
                order.getOutChannel());
        BigDecimal amount = order.getAmount();

        // 成本金额
        if (payCfgComp != null && payCfgComp.getCostRate() != null) {
            //费率单位
            Integer costRateUnit = payCfgComp.getCostRateUnit();
            if (costRateUnit.equals(PaymentRateUnit.PRECENT.id())) {
                order.setCostAmount(ParamUtil.multBig(amount,
                        payCfgComp.getCostRate().divide(new BigDecimal(100))));
            } else if (costRateUnit.equals(PaymentRateUnit.YUAN.id())) {
                order.setCostAmount(payCfgComp.getCostRate());
            }
        } else {
            order.setCostAmount(BigDecimal.ZERO);
        }

        int crtDate = order.getCrtDate();
        if (ParamUtil.isNotEmpty(order.getMsg()) && order.getMsg().length() > 50) {
            order.setMsg(order.getMsg().substring(0, 50));
        }
        payOrderAcpDao.save(order);

        Integer userType = order.getUserType();
        if (OrderState.succ.id() == orderState) {

            RecordFoundAcctDO rdFoundAcct = null;

            BigDecimal dfAmount = order.getQhAmount();

            // 增加平台资金账户可用余额以及流水
            rdFoundAcct = payHandlerService.availBalForPlatAdd(order,
                    dfAmount.subtract(order.getCostAmount()),
                    FeeType.platWithdrawIn.id(), OrderType.withdraw.id());
            rdFoundAcct.setCrtDate(crtDate);
            rdFoundAvailAcctDao.save(rdFoundAcct);

            // 增加平台资金账户余额以及流水
            rdFoundAcct = payHandlerService.balForPlatAdd(order, dfAmount.subtract(order.getCostAmount()),
                    FeeType.platWithdrawIn.id(), OrderType.withdraw.id());
            rdFoundAcct.setCrtDate(crtDate);
            rdFoundAcctDao.save(rdFoundAcct);

            // 减去第三方支付公司资金账户可用余额以及流水
            RecordPayMerchBalDO rdPayMerchAcct = payHandlerService.availBalForPayMerchSub(order, order.getAmount(),
                    FeeType.payMerchAcp.id(), OrderType.withdraw.id());
            rdPayMerchAcct.setCrtDate(crtDate);
            rdPayMerchAvailBalDao.save(rdPayMerchAcct);

            // 减去第三方支付公司资金账户可用余额以及流水    减手续费
            rdPayMerchAcct = payHandlerService.availBalForPayMerchSub(order, order.getCostAmount(),
                    FeeType.payMerchAcpHand.id(), OrderType.withdraw.id());
            rdPayMerchAcct.setCrtDate(crtDate);
            rdPayMerchAvailBalDao.save(rdPayMerchAcct);

            // 减去第三方支付公司资金账户余额以及流水
            rdPayMerchAcct = payHandlerService.balForPayMerchSub(order, order.getAmount(),
                    FeeType.payMerchAcp.id(), OrderType.withdraw.id());
            rdPayMerchAcct.setCrtDate(crtDate);
            rdPayMerchBalDao.save(rdPayMerchAcct);

            // 减去第三方支付公司资金账户余额以及流水    减手续费
            rdPayMerchAcct = payHandlerService.balForPayMerchSub(order, order.getCostAmount(),
                    FeeType.payMerchAcpHand.id(), OrderType.withdraw.id());
            rdPayMerchAcct.setCrtDate(crtDate);
            rdPayMerchBalDao.save(rdPayMerchAcct);
            return true;
        } else {

            if (UserType.merch.id() == userType) {
                List<RecordMerchBalDO> rdMerchAvailBalList = new ArrayList<>();
                //返还可用余额
                RecordMerchBalDO rdMerchAvailBal = payHandlerService.availBalForMerchAdd(order, order.getAmount(),
                        FeeType.withdrawFail.id(), OrderType.withdraw.id());
                rdMerchAvailBal.setCrtDate(crtDate);
                rdMerchAvailBalList.add(rdMerchAvailBal);

                rdMerchAvailBal = payHandlerService.availBalForMerchAdd(order, order.getQhAmount(),
                        FeeType.merchWithDrawHandFee.id(), OrderType.withdraw.id());
                rdMerchAvailBal.setCrtDate(crtDate);
                rdMerchAvailBalList.add(rdMerchAvailBal);

                rdMerchAvailBalDao.saveBatch(rdMerchAvailBalList);

                //返还余额
                RecordMerchBalDO rdMerchBal = payHandlerService.balForMerchAdd(order, order.getAmount(),
                        FeeType.withdrawFail.id(), OrderType.withdraw.id());
                rdMerchBal.setCrtDate(crtDate);
                rdMerchBalDao.save(rdMerchBal);

                rdMerchBal = payHandlerService.balForMerchAdd(order, order.getQhAmount(),
                        FeeType.merchWithDrawHandFee.id(), OrderType.withdraw.id());
                rdMerchBal.setCrtDate(crtDate);
                rdMerchBalDao.save(rdMerchBal);

            } else if (UserType.agent.id() == userType || UserType.subAgent.id() == userType) {

                RecordFoundAcctDO rdAgentAvailBal = payHandlerService.availBalForAgentAdd(order, order.getAmount(),
                        order.getMerchNo(), FeeType.withdrawFail.id(), OrderType.withdraw.id());
                rdAgentAvailBal.setCrtDate(crtDate);
                rdFoundAvailAcctDao.save(rdAgentAvailBal);

                rdAgentAvailBal = payHandlerService.availBalForAgentAdd(order, order.getQhAmount(),
                        order.getMerchNo(), FeeType.agentWithDrawHandFee.id(), OrderType.withdraw.id());
                rdAgentAvailBal.setCrtDate(crtDate);
                rdFoundAvailAcctDao.save(rdAgentAvailBal);

                rdFoundAcctDao.save(payHandlerService.balForAgentAdd(order, order.getAmount(), order.getMerchNo(),
                        FeeType.withdrawFail.id(), OrderType.withdraw.id()));
                rdFoundAcctDao.save(payHandlerService.balForAgentAdd(order, order.getQhAmount(), order.getMerchNo(),
                        FeeType.agentWithDrawHandFee.id(), OrderType.withdraw.id()));

            }
            return true;
        }

    }

    /**
     * @param order
     * @Description 保存代付订单数据
     */
    @Transactional
    boolean saveOrderAcpData(Order order) {
        Integer orderState = order.getOrderState();
        // 商户信息
        Merchant merchant = merchantService.get(order.getMerchNo());
        // 支付通道信息
        PayConfigCompanyDO payCfgComp = payCfgCompService.get(order.getPayCompany(), order.getPayMerch(),
                order.getOutChannel());
        BigDecimal amount = order.getAmount();
        //费率单位
        Integer costRateUnit = payCfgComp.getCostRateUnit();
        // 成本金额
        if (payCfgComp.getCostRate() != null) {
            if (costRateUnit.equals(PaymentRateUnit.PRECENT.id())) {
                order.setCostAmount(ParamUtil.multBig(amount,
                        payCfgComp.getCostRate().divide(new BigDecimal(100))));
            } else if (costRateUnit.equals(PaymentRateUnit.YUAN.id())) {
                order.setCostAmount(payCfgComp.getCostRate());
            }
        } else {
            order.setCostAmount(BigDecimal.ZERO);
        }

        // 商户金额
        Integer jfUnit = null;
        // 代理金额
        BigDecimal feeRate = null;
        Agent agent = agentService.get(merchant.getParentAgent());

        BigDecimal agentAmount = BigDecimal.ZERO;

        Map<String, BigDecimal> paidAcpMap = agent.getCoinRate().get(order.getOutChannel());
        feeRate = new BigDecimal(paidAcpMap.get(PayConstants.PAYMENT_RATE).toString());
        jfUnit = Integer.valueOf(paidAcpMap.get(PayConstants.PAYMENT_UNIT).toString());
        if (feeRate != null) {
            if (jfUnit.equals(PaymentRateUnit.PRECENT.id())) {
                feeRate = feeRate.divide(new BigDecimal(100));
                agentAmount = ParamUtil.multSmall(amount, feeRate);
            } else if (jfUnit.equals(PaymentRateUnit.YUAN.id())) {
                agentAmount = feeRate;
            }
        }
        //上级代理
        String parentAgentNumber = agent.getParentAgent();
        if (agent.getLevel() == AgentLevel.two.id()) {
            order.setSubAgentAmount(order.getQhAmount().subtract(agentAmount));

            feeRate = null;
            Agent paramAgent = agentService.get(parentAgentNumber);
            paidAcpMap = paramAgent.getCoinRate().get(order.getOutChannel());
            feeRate = new BigDecimal(paidAcpMap.get(PayConstants.PAYMENT_RATE).toString());
            jfUnit = Integer.valueOf(paidAcpMap.get(PayConstants.PAYMENT_UNIT).toString());
            if (feeRate != null) {
                BigDecimal parentAgentAmount = BigDecimal.ZERO;
                if (jfUnit.equals(PaymentRateUnit.PRECENT.id())) {
                    feeRate = feeRate.divide(new BigDecimal(100));
                    parentAgentAmount = ParamUtil.multSmall(amount, feeRate);
                } else if (jfUnit.equals(PaymentRateUnit.YUAN.id())) {
                    parentAgentAmount = feeRate;
                }
                order.setAgentAmount(agentAmount.subtract(parentAgentAmount));
                agentAmount = parentAgentAmount;
            }
        } else {
            order.setAgentAmount(order.getQhAmount().subtract(agentAmount));
        }

        int crtDate = order.getCrtDate();
        if (ParamUtil.isNotEmpty(order.getMsg()) && order.getMsg().length() > 50) {
            order.setMsg(order.getMsg().substring(0, 50));
        }
        payOrderAcpDao.save(order);
        if (OrderState.succ.id() == orderState) {

            RecordFoundAcctDO rdFoundAcct = null;
            // 增加代理可用/余额以及流水
            if (agent.getLevel() == AgentLevel.two.id()) {
                rdFoundAcct = payHandlerService.availBalForAgentAdd(order, order.getSubAgentAmount(),
                        agent.getAgentNumber(), FeeType.agentAcpIn.id(), OrderType.acp.id());
                rdFoundAcct.setCrtDate(crtDate);
                rdFoundAvailAcctDao.save(rdFoundAcct);

                rdFoundAcct = payHandlerService.balForAgentAdd(order, order.getSubAgentAmount(),
                        agent.getAgentNumber(), FeeType.agentAcpIn.id(), OrderType.acp.id());
                rdFoundAcct.setCrtDate(crtDate);
                rdFoundAcctDao.save(rdFoundAcct);

                //上级代理
                rdFoundAcct = payHandlerService.availBalForAgentAdd(order, order.getAgentAmount(),
                        parentAgentNumber,
                        FeeType.agentAcpIn.id(), OrderType.acp.id());
                rdFoundAcct.setCrtDate(crtDate);
                rdFoundAvailAcctDao.save(rdFoundAcct);

                rdFoundAcct = payHandlerService.balForAgentAdd(order, order.getAgentAmount(), parentAgentNumber,
                        FeeType.agentAcpIn.id(), OrderType.acp.id());
                rdFoundAcct.setCrtDate(crtDate);
                rdFoundAcctDao.save(rdFoundAcct);
            } else {
                rdFoundAcct = payHandlerService.availBalForAgentAdd(order, order.getAgentAmount(),
                        agent.getAgentNumber(), FeeType.agentAcpIn.id(), OrderType.acp.id());
                rdFoundAcct.setCrtDate(crtDate);
                rdFoundAvailAcctDao.save(rdFoundAcct);

                rdFoundAcct = payHandlerService.balForAgentAdd(order, order.getAgentAmount(),
                        agent.getAgentNumber(),
                        FeeType.agentAcpIn.id(), OrderType.acp.id());
                rdFoundAcct.setCrtDate(crtDate);
                rdFoundAcctDao.save(rdFoundAcct);
            }

            // 增加平台资金账户可用余额以及流水
            rdFoundAcct = payHandlerService.availBalForPlatAdd(order,
                    agentAmount.subtract(order.getCostAmount()),
                    FeeType.platAcpIn.id(), OrderType.acp.id());
            rdFoundAcct.setCrtDate(crtDate);
            rdFoundAvailAcctDao.save(rdFoundAcct);

            // 增加平台资金账户余额以及流水
            rdFoundAcct = payHandlerService.balForPlatAdd(order, agentAmount.subtract(order.getCostAmount()),
                    FeeType.platAcpIn.id(), OrderType.acp.id());
            rdFoundAcct.setCrtDate(crtDate);
            rdFoundAcctDao.save(rdFoundAcct);

            // 减去第三方支付公司资金账户余额以及流水
            RecordPayMerchBalDO rdPayMerchAcct = payHandlerService.balForPayMerchSub(order, order.getAmount(),
                    FeeType.payMerchAcp.id(), OrderType.pay.id());
            rdPayMerchAcct.setCrtDate(crtDate);
            rdPayMerchBalDao.save(rdPayMerchAcct);

            // 减去第三方支付公司资金账户余额以及流水    减手续费
            rdPayMerchAcct = payHandlerService.balForPayMerchSub(order, order.getCostAmount(),
                    FeeType.payMerchAcpHand.id(), OrderType.pay.id());
            rdPayMerchAcct.setCrtDate(crtDate);
            rdPayMerchBalDao.save(rdPayMerchAcct);

            // 减去第三方支付公司资金账户可用余额以及流水
            rdPayMerchAcct = payHandlerService.availBalForPayMerchSub(order, order.getAmount(),
                    FeeType.payMerchAcp.id(), OrderType.pay.id());
            rdPayMerchAcct.setCrtDate(crtDate);
            rdPayMerchAvailBalDao.save(rdPayMerchAcct);

            // 减去第三方支付公司资金账户可用余额以及流水    减手续费
            rdPayMerchAcct = payHandlerService.availBalForPayMerchSub(order, order.getCostAmount(),
                    FeeType.payMerchAcpHand.id(), OrderType.pay.id());
            rdPayMerchAcct.setCrtDate(crtDate);
            rdPayMerchAvailBalDao.save(rdPayMerchAcct);
            return true;
        } else {

            List<RecordMerchBalDO> rdMerchAvailBalList = new ArrayList<>();
            //返还可用余额
            RecordMerchBalDO rdMerchAvailBal = payHandlerService.availBalForMerchAdd(order, order.getAmount(),
                    FeeType.merchAcpFail.id(), OrderType.acp.id());
            rdMerchAvailBal.setCrtDate(crtDate);
            rdMerchAvailBalList.add(rdMerchAvailBal);

            rdMerchAvailBal = payHandlerService.availBalForMerchAdd(order, order.getQhAmount(),
                    FeeType.merchAcpHandFee.id(), OrderType.acp.id());
            rdMerchAvailBal.setCrtDate(crtDate);
            rdMerchAvailBalList.add(rdMerchAvailBal);

            //返还余额
            RecordMerchBalDO rdMerchBal = payHandlerService.balForMerchAdd(order, order.getAmount(),
                    FeeType.merchAcpFail.id(), OrderType.acp.id());
            rdMerchBal.setCrtDate(crtDate);
            rdMerchBalDao.save(rdMerchBal);

            rdMerchBal = payHandlerService.balForMerchAdd(order, order.getQhAmount(), FeeType.merchAcpHandFee.id(),
                    OrderType.acp.id());
            rdMerchBal.setCrtDate(crtDate);
            rdMerchBalDao.save(rdMerchBal);

            rdMerchAvailBalDao.saveBatch(rdMerchAvailBalList);

            return true;
        }
    }


    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayService#chargeDataMsg(java.lang.String, java.lang.String)
     */
    @Override
    public void chargeDataMsg(String merchNo, String orderNo) {
        payQrService.chargeDataMsg(merchNo, orderNo);
    }


    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayService#syncOrder(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public R syncOrder(String merchNo, String orderNo, String businessNo) {
        Merchant merchant = merchantService.get(merchNo);
        if (merchant == null) {
            return R.error("商户不存在 " + merchNo);
        }
        RLock lock = RedissonLockUtil.getOrderLock(merchNo + RedisConstants.link_symbol + orderNo);
        if (lock.tryLock()) {
            Order order = null;
            String msg = Constant.result_msg_succ;
            try {
                order = RedisUtil.getOrder(merchNo, orderNo);
                boolean dataFlag = false;
                if (order == null) {
                    order = payOrderDao.get(orderNo, merchNo);
                    dataFlag = true;
                }
                if (order == null) {
                    return R.error(merchNo + "," + orderNo + "支付订单不存在！");
                }
				/*if(OrderState.succ.id() == order.getOrderState()){
					return R.error(merchNo + "," + orderNo + "订单已同步！");
				}*/
                // 无支付成功结果去第三方查询
                if (!dataFlag && OrderState.succ.id() != order.getOrderState()) {
//                    R r;
//                    if (OutChannel.jfDesc().containsKey(order.getOutChannel())) {
                    R r = payQrService.syncOrder(merchant, order, businessNo);
//                    } else {
//                        r = payBaseService.query(order);
//                    }
                    if (R.ifError(r)) {
                        return r;
                    }
                    msg = (String) r.get(Constant.result_msg);
                    order.setMsg(msg);
                    RedisUtil.setOrder(order);
                }

//                if (!dataFlag && OrderState.succ.id() == order.getOrderState()) {
                String result = orderNotify(order, businessNo);
//                    if (payOrderDao.get(orderNo, merchNo) == null) {
//                        if (Constant.result_msg_succ.equalsIgnoreCase(result) || Constant.result_msg_ok
//                        .equalsIgnoreCase(result)) {
                lock.unlock();
                RedisMsg.orderDataMsg(merchNo, orderNo);
//                        } else {
//                            if (!RedisUtil.setKeyEventExpired(RedisConstants.cache_keyevent_ord, merchNo,
//                                    orderNo)) {
//                                lock.unlock();
//                                RedisMsg.orderDataMsg(merchNo, orderNo);
//                            }
//                        }
//                    } else {
//                        logger.info("手动同步，订单已存在，不发通知保存.{},{},通知结果：{}", merchNo, orderNo, result);
//                    }
//                }
                return R.ok("手动同步操作成功,订单状态：" + OrderState.desc().get(order.getOrderState()));
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            return R.error("同步过于繁忙，请稍后再试！");
        }
    }


    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayService#syncOrderAcp(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public R syncOrderAcp(String merchNo, String orderNo, String businessNo) {
        Merchant merchant = merchantService.get(merchNo);
        if (merchant == null) {
            return R.error("商户不存在 " + merchNo);
        }
        RLock lock = RedissonLockUtil.getOrderAcpLock(merchNo + RedisConstants.link_symbol + orderNo);
        if (lock.tryLock()) {
            Order order = null;
            String msg = Constant.result_msg_succ;
            try {
                order = RedisUtil.getOrderAcp(merchNo, orderNo);
                boolean dataFlag = false;
                if (order == null) {
                    order = payOrderAcpDao.get(orderNo, merchNo);
                    dataFlag = true;
                }
                if (order == null) {
                    return R.error(merchNo + "," + orderNo + "支付订单不存在！");
                }
                // 无支付成功结果去第三方查询
                if (!dataFlag && OrderState.succ.id() != order.getOrderState()) {
                    R r = payBaseService.acpQuery(order);
                    if (R.ifError(r)) {
                        return r;
                    }
                    msg = (String) r.get(Constant.result_msg);
                    order.setMsg(msg);
                    RedisUtil.setOrderAcp(order);
                }
                //提现类型的订单直接 手动同步返回
                if (Integer.valueOf(OrderType.withdraw.id()).equals(order.getOrderType())) {
                    lock.unlock();
                    RedisMsg.orderAcpDataMsg(merchNo, orderNo);
                    return R.ok("手动同步操作成功,订单状态：" + OrderState.desc().get(order.getOrderState()));
                }
                if (!dataFlag && OrderState.succ.id() == order.getOrderState()) {
                    String result = orderAcpNotify(order, businessNo);
                    if (payOrderDao.get(orderNo, merchNo) == null) {
                        if (Constant.result_msg_succ.equalsIgnoreCase(result) || Constant.result_msg_ok.equalsIgnoreCase(result)) {
                            lock.unlock();
                            RedisMsg.orderAcpDataMsg(merchNo, orderNo);
                        } else {
                            if (!RedisUtil.setKeyEventExpired(RedisConstants.cache_keyevent_ord, merchNo,
                                    orderNo)) {
                                lock.unlock();
                                RedisMsg.orderAcpDataMsg(merchNo, orderNo);
                            }
                        }
                    } else {
                        logger.info("手动同步，订单已存在，不发通知保存.{},{},通知结果：{}", merchNo, orderNo, result);
                    }
                }
                return R.ok("手动同步操作成功,订单状态：" + OrderState.desc().get(order.getOrderState()));
            } finally {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            return R.error("同步过于繁忙，请稍后再试！");
        }
    }


    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayService#withdraw(com.qh.pay.api.Order)
     */
    @Override
    public R withdraw(Order order) {
        String merchNo = order.getMerchNo();
        String orderNo = ParamUtil.getOrderId();
        order.setOrderNo(orderNo);
        RLock lock = RedissonLockUtil.getOrderAcpLock(merchNo + RedisConstants.link_symbol + orderNo);
        if (lock.tryLock()) {
            try {
                if (RedisUtil.getOrderAcp(merchNo, orderNo) != null) {
                    return R.error("系统繁忙，请稍后再试");
                }
                // 检查提现订单信息
                String checkResult = payHandlerService.checkUserWithDrawOrder(order);
                order.setOrderType(OrderType.withdraw.id());
                if (ParamUtil.isNotEmpty(checkResult)) {
                    logger.error(checkResult);
                    return R.error(checkResult);
                }
				/*R r = checkAcpCfgComp(order);
				if(R.ifError(r)){
					return r;
				}*/
                //保存银联信息
                userBankService.save(order.getMobile(), merchNo, order);

                order.setUserId(merchNo);
                Integer userType = order.getUserType();
                PayAcctBal pab = null;
                Integer crtDate = null;
                Agent agent = null;
                List<RecordMerchBalDO> rdMerchAvailBalList = new ArrayList<RecordMerchBalDO>();
                List<RecordFoundAcctDO> rdFoundAvailBalList = new ArrayList<RecordFoundAcctDO>();
                if (UserType.merch.id() == userType) {

                    // 检查商户余额 -----》商户可用余额
                    RLock merchLock = RedissonLockUtil.getBalMerchLock(merchNo);
                    try {
                        merchLock.lock();
                        // 商户信息
                        Merchant merchant = merchantService.get(merchNo);
                        //计算出  商户代付  手续费费率
//						Map<String,BigDecimal> paidMap = merchant.getCoinRate().get(OutChannel.acp.name());
//						BigDecimal jfRate = new BigDecimal(paidMap.get(PayConstants.PAYMENT_RATE).toString());
//						Integer jfUnit = Integer.valueOf(paidMap.get(PayConstants.PAYMENT_UNIT).toString());
//						if(jfRate != null){
//							if(jfUnit.equals(PaymentRateUnit.PRECENT.id())) {
//								jfRate = jfRate.divide(new BigDecimal(100));
//								order.setQhAmount(ParamUtil.multBig(amount, jfRate));
//							}else if(jfUnit.equals(PaymentRateUnit.YUAN.id())) {
//								order.setQhAmount(jfRate);
//							}
//						}
                        //变动金额
                        order.setQhAmount(ParamUtil.multBig(order.getAmount(), new BigDecimal("0.01")));
                        RecordMerchBalDO rdMerchAvailBal = PayService.initRdMerchBal(order,
                                FeeType.withdrawOut.id(),
                                OrderType.withdraw.id(), ProfitLoss.loss.id());
                        rdMerchAvailBal.setCrtDate(DateUtil.getCurrentTimeInt());
                        pab = RedisUtil.getMerchBal(merchNo);
                        if (pab == null || pab.getAvailBal().compareTo(order.getAmount()) < 0) {
                            return R.error("商户号 " + merchNo + ",可用余额不足！");
                        }

                        rdMerchAvailBal.setTranAmt(order.getAmount().subtract(order.getQhAmount()));
                        if (rdMerchAvailBal.getTranAmt().compareTo(BigDecimal.ZERO) < 0) {
                            return R.error("商户号 " + merchNo + ",提现金额不能小于：0");
                        }

                        payHandlerService.availBalForMerchSub(order, rdMerchAvailBal, pab);
                        RedisUtil.setMerchBal(pab);
                        rdMerchAvailBalList.add(rdMerchAvailBal);

                        //扣除商户提现手续费
                        rdMerchAvailBal = PayService.initRdMerchBal(order, FeeType.merchWithDrawHandFee.id(),
                                OrderType.withdraw.id(), ProfitLoss.loss.id());
                        rdMerchAvailBal.setCrtDate(DateUtil.getCurrentTimeInt());
                        rdMerchAvailBal.setTranAmt(order.getQhAmount());

                        payHandlerService.availBalForMerchSub(order, rdMerchAvailBal, pab);
                        RedisUtil.setMerchBal(pab);

                        rdMerchAvailBalList.add(rdMerchAvailBal);

                        crtDate = rdMerchAvailBal.getCrtDate();
                    } finally {
                        merchLock.unlock();
                    }
                } else if (UserType.agent.id() == userType || UserType.subAgent.id() == userType) {
                    pab = RedisUtil.getAgentBal(merchNo);
                    agent = agentService.get(merchNo);
//					Map<String,BigDecimal> paidAcpMap = agent.getCoinRate().get(OutChannel.acp.name());
//					BigDecimal feeRate = new BigDecimal(paidAcpMap.get(PayConstants.PAYMENT_RATE).toString());
//					Integer jfUnit = Integer.valueOf(paidAcpMap.get(PayConstants.PAYMENT_UNIT).toString());
//					if (feeRate != null) {
//						if(jfUnit.equals(PaymentRateUnit.PRECENT.id())) {
//							feeRate = feeRate.divide(new BigDecimal(100));
//							order.setQhAmount(ParamUtil.multSmall(amount, feeRate));
//						}else if(jfUnit.equals(PaymentRateUnit.YUAN.id())) {
//							order.setQhAmount(feeRate);
//						}
//					}
                    order.setQhAmount(ParamUtil.multBig(order.getAmount(), new BigDecimal("0.01")));
                    RecordFoundAcctDO rdAgentAvailBal = PayService.initRdFoundAcct(order, FeeType.withdrawOut.id(),
                            OrderType.withdraw.id(), ProfitLoss.loss.id());
                    rdAgentAvailBal.setCrtDate(DateUtil.getCurrentTimeInt());
                    pab = RedisUtil.getAgentBal(agent.getAgentNumber());
                    if (pab == null || pab.getAvailBal().compareTo(order.getAmount()) < 0) {
                        return R.error("代理 " + merchNo + ",可用余额不足！");
                    }
                    rdAgentAvailBal.setTranAmt(order.getAmount().subtract(order.getQhAmount()));
                    if (rdAgentAvailBal.getTranAmt().compareTo(BigDecimal.ZERO) < 0) {
                        return R.error("商户号 " + merchNo + ",提现金额不能小于：" + order.getQhAmount());
                    }
                    rdAgentAvailBal.setUsername(agent.getAgentNumber());

                    payHandlerService.availBalForAgentSub(order, rdAgentAvailBal, pab);
                    RedisUtil.setAgentBal(pab);
                    ;

                    rdFoundAvailBalList.add(rdAgentAvailBal);

                    //扣除商户提现手续费
                    rdAgentAvailBal = PayService.initRdFoundAcct(order, FeeType.agentWithDrawHandFee.id(),
                            OrderType.withdraw.id(), ProfitLoss.loss.id());
                    rdAgentAvailBal.setCrtDate(DateUtil.getCurrentTimeInt());
                    rdAgentAvailBal.setTranAmt(order.getQhAmount());
                    payHandlerService.availBalForAgentSub(order, rdAgentAvailBal, pab);
                    RedisUtil.setAgentBal(pab);


                    rdFoundAvailBalList.add(rdAgentAvailBal);

                    crtDate = rdAgentAvailBal.getCrtDate();
                }
                order.setAmount(order.getAmount().subtract(order.getQhAmount()));
                PayAuditDO payAudit = PayService.initPayAudit(order, AuditType.order_withdraw.id());
                int count = 0;
                try {
                    payAudit.setCrtTime(crtDate);
                    count = payAuditDao.save(payAudit);
                } catch (Exception e) {
                    count = 0;
                }
                try {
                    if (rdMerchAvailBalList.size() > 0) {
                        //保存商户可用余额流水
                        rdMerchAvailBalDao.saveBatch(rdMerchAvailBalList);
                    }
                    if (rdFoundAvailBalList.size() > 0) {
                        //保存代理可用余额流水
                        rdFoundAvailAcctDao.saveBatch(rdFoundAvailBalList);
                    }

                } catch (Exception e) {
                    logger.error("提现保存可用余额流水记录失败！{}，{}", merchNo, orderNo);
                }
                if (count == 1) {
                    RedisUtil.setOrderAcp(order);
                    //如果成功，商户余额减少
                    try {
                        if (UserType.merch.id() == userType) {
                            rdMerchBalDao.save(payHandlerService.balForMerchSub(order, order.getAmount(),
                                    FeeType.withdrawOut.id(), OrderType.withdraw.id()));
                            rdMerchBalDao.save(payHandlerService.balForMerchSub(order, order.getQhAmount(),
                                    FeeType.merchWithDrawHandFee.id(), OrderType.withdraw.id()));
                        } else if (UserType.agent.id() == userType || UserType.subAgent.id() == userType) {
                            rdFoundAcctDao.save(payHandlerService.balForAgentSub(order, order.getAmount(),
                                    agent.getAgentNumber(), FeeType.withdrawOut.id(), OrderType.withdraw.id()));
                            rdFoundAcctDao.save(payHandlerService.balForAgentSub(order, order.getQhAmount(),
                                    agent.getAgentNumber(), FeeType.agentWithDrawHandFee.id(),
                                    OrderType.withdraw.id()));
                        }
                    } catch (Exception e) {
                        logger.error("代付保存余额流水记录失败！{}，{}", merchNo, orderNo);
                    }
                    Map<String, String> data = PayService.initRspData(order);
                    data.put(OrderParamKey.orderState.name(), String.valueOf(OrderState.init.id()));
                    return R.okData(data).put(Constant.result_msg, "提现请求已受理!");
                } else {// 出错了返还可用余额
                    if (UserType.merch.id() == userType) {
                        RecordMerchBalDO rdMerchAvailBal = payHandlerService.availBalForMerchAdd(order,
                                order.getAmount(), FeeType.withdrawFail.id(), OrderType.withdraw.id());
                        rdMerchAvailBal.setCrtDate(payAudit.getCrtTime());
                        rdMerchAvailBalDao.save(rdMerchAvailBal);

                        rdMerchAvailBal = payHandlerService.availBalForMerchAdd(order, order.getQhAmount(),
                                FeeType.merchWithDrawHandFee.id(), OrderType.withdraw.id());
                        rdMerchAvailBal.setCrtDate(payAudit.getCrtTime());
                        rdMerchAvailBalDao.save(rdMerchAvailBal);
                    } else if (UserType.agent.id() == userType || UserType.subAgent.id() == userType) {
                        RecordFoundAcctDO rdAgentAvailBal = payHandlerService.availBalForAgentAdd(order,
                                order.getAmount(), agent.getAgentNumber(), FeeType.withdrawFail.id(),
                                OrderType.withdraw.id());
                        rdAgentAvailBal.setCrtDate(payAudit.getCrtTime());
                        rdFoundAvailAcctDao.save(rdAgentAvailBal);

                        rdAgentAvailBal = payHandlerService.availBalForAgentAdd(order, order.getQhAmount(),
                                agent.getAgentNumber(), FeeType.agentWithDrawHandFee.id(), OrderType.withdraw.id());
                        rdAgentAvailBal.setCrtDate(payAudit.getCrtTime());
                        rdFoundAvailAcctDao.save(rdAgentAvailBal);
                    }

                    return R.error("代付异常！");
                }
            } finally {
                lock.unlock();
            }
        } else {
            return R.error("系统繁忙，请稍后再试");
        }
    }


    /**
     * @param order
     * @Description
     */
    private R checkAcpCfgComp(Order order) {
        R r = checkCfgCompDF(order);
        if (R.ifError(r)) {
            return r;
        }
        //如果满足需要行号的支付公司则需要传递银联行号参数
        if (Integer.valueOf(YesNoType.yes.id()).equals(PayCompany.companyUnionPay(order.getPayCompany())) && (
                ParamUtil.isEmpty(order.getUnionpayNo()) || ParamUtil.isEmpty(order.getBankBranch()))) {
            return R.error("未找到银联行号信息参数");
        }
        return R.ok();
    }

    /**
     * @param order
     * @return
     * @Description 代付通道 选择
     */
    private R checkCfgCompDF(Order order) {

        String payCompany = "";
        String payMerch = "";
        String callbackDomain = "";
        Merchant merchant = merchantService.get(order.getMerchNo());
        Map<String, String> paidChannelMap = new HashMap<>();
        //如果为商户指定了通道，则直接使用指定通道
        payCompany = paidChannelMap.get("payCompany");
        payMerch = paidChannelMap.get("payMerch");
        if (StringUtils.isNotBlank(payMerch) && StringUtils.isNotBlank(payCompany)) {
            PayConfigCompanyDO payCfgComp = payCfgCompService.get(payCompany, payMerch, order.getOutChannel());
            if (payCfgComp != null) {
                if (payCfgComp.getIfClose() != 0) {
                    return R.error(order.getMerchNo() + "," + order.getOutChannel() + "通道关闭！");
                }
                callbackDomain = payCfgComp.getCallbackDomain();
            } else {
                return R.error(order.getMerchNo() + "," + order.getOutChannel() + "通道不存在！");
            }

        } else {
            payCompany = "";
            payMerch = "";
            Integer mPayChannelType = merchant.getPayChannelType();
            //判断商户是否有分配  通道分类   没有分配不支持支付
            if (mPayChannelType == null || !PayChannelType.desc().containsKey(mPayChannelType)) {
                return R.error(order.getMerchNo() + "," + order.getOutChannel() + "未分配支付通道");
            }

            List<Object> payCfgComps = payCfgCompService.getPayCfgCompByOutChannel(order.getOutChannel());
            if (payCfgComps == null || payCfgComps.size() == 0) {
                return R.error(order.getMerchNo() + "," + order.getOutChannel() + "通道配置错误！");
            }

            List<PayConfigCompanyDO> pccList = new ArrayList<PayConfigCompanyDO>();
            PayConfigCompanyDO payCfgComp = null;
            for (Object object : payCfgComps) {
                payCfgComp = (PayConfigCompanyDO) object;
                if (payCfgComp.getIfClose() == 0) {
                    Integer cPayChannelType = payCfgComp.getPayChannelType();
                    if (cPayChannelType != null && cPayChannelType.equals(mPayChannelType)) {
                        //将与商户  同类的通道 找出来
                        pccList.add(payCfgComp);
                    }
                }
            }

            if (pccList.size() <= 0) {
                //当前商户没有 同类通道
                return R.error(order.getMerchNo() + "," + order.getOutChannel() + "未找到相应的支付通道");
            }
            payCfgComp = null;
            BigDecimal amount = order.getAmount();
            BigDecimal qhAmount = order.getQhAmount();
            amount = amount.add(qhAmount);
            PayAcctBal pab = RedisUtil.getMerchBal(order.getMerchNo());
            Map<String, BigDecimal> companyPayAvailBal = pab.getCompanyPayAvailBal();
            if (companyPayAvailBal != null) {
                for (PayConfigCompanyDO payConfigCompanyDO : pccList) {
                    String payCompanyName = payConfigCompanyDO.getCompany();
                    BigDecimal singleAvailBal =
                            companyPayAvailBal.get(payCompanyName + RedisConstants.link_symbol + payConfigCompanyDO.getPayMerch());
                    logger.info("{}商户在支付公司{}下,商户号为{}的可用余额剩余:{},商户此次支出金额为:{}", order.getMerchNo(), payCompanyName,
                            payConfigCompanyDO.getPayMerch(), singleAvailBal, amount);
                    if (singleAvailBal != null && amount.compareTo(singleAvailBal) <= 0) {
                        //如果订单金额小于当前商户在当前支付公司交易的可用余额， 则使用此通道
                        payCfgComp = payConfigCompanyDO;
                        break;
                    }
                }
            }
            //TODO 如果上面的代码调试通过则不需要下面的4行代码
            payCfgComp = pccList.get(0);

            if (payCfgComp == null) {
                return R.error(order.getMerchNo() + "未找到相应的支付通道或者可用余额不足");
            }
            Integer maxPayAmt = payCfgComp.getMaxPayAmt();
            Integer minPayAmt = payCfgComp.getMinPayAmt();
            if (maxPayAmt != null && new BigDecimal(maxPayAmt).compareTo(order.getAmount()) == -1) {
                return R.error("单笔最高限额:" + maxPayAmt + "元");
            }
            if (minPayAmt != null && new BigDecimal(minPayAmt).compareTo(order.getAmount()) == 1) {
                return R.error("单笔最低限额为:" + minPayAmt + "元");
            }
            payCompany = payCfgComp.getCompany();
            payMerch = payCfgComp.getPayMerch();
            callbackDomain = payCfgComp.getCallbackDomain();
        }
        order.setPayCompany(payCompany);
        order.setPayMerch(payMerch);
        order.setCallbackDomain(callbackDomain);
        return R.ok();

    }


    /* (非 Javadoc)
     * Description:
     * @see com.qh.pay.service.PayService#offlineTransfer(java.lang.String, java.lang.String, java.lang.Integer)
     */
    @Override
    public R offlineTransfer(String orderNo, String merchNo, Integer auditType) {
        RLock lock = RedissonLockUtil.getOrderAcpLock(merchNo, orderNo);
        if (lock.tryLock()) {
            try {
                Order order = RedisUtil.getOrderAcp(merchNo, orderNo);
                if (order == null) {
                    logger.info("代付订单数据保存失败，订单不存在，{}，{}", merchNo, orderNo);
                    return R.error("订单不存在");
                }
                if (!Integer.valueOf(OrderType.withdraw.id()).equals(order.getOrderType())) {
                    return R.error("只有提现类订单才支持线下转账");
                }
                Integer orderState = order.getOrderState();
                if (orderState == OrderState.init.id()) {
                    order.setOrderState(OrderState.succ.id());
                    order.setRealAmount(order.getAmount());
                    if (this.saveOrderWithdrawData(order)) {
                        RedisUtil.removeOrderAcp(merchNo, orderNo);
                        logger.info("代付订单数据保存成功，{}，{}", merchNo, orderNo);
                        return R.ok("线下转账成功");
                    }
                    return R.ok("线下转账失败");
                } else {
                    logger.info("代付订单数据保存失败，订单状态'{}'不满足条件，{}，{}", orderState, merchNo, orderNo);
                    return R.ok("线下转账失败，订单状态不满足条件");
                }
            } finally {
                lock.unlock();
            }
        } else {
            return R.error("系统繁忙，请稍后再试");
        }
    }


}
